diff --git a/.github/alternate_byond_versions.txt b/.github/alternate_byond_versions.txt index 111e573827e8e..e1496d438cdc4 100644 --- a/.github/alternate_byond_versions.txt +++ b/.github/alternate_byond_versions.txt @@ -5,4 +5,4 @@ # Format is version: map # Example: # 500.1337: runtimestation -515.1621: runtimestation +515.1627: runtimestation diff --git a/.github/workflows/auto_changelog.yml b/.github/workflows/auto_changelog.yml index 13b580547011f..f47c2936980fe 100644 --- a/.github/workflows/auto_changelog.yml +++ b/.github/workflows/auto_changelog.yml @@ -14,7 +14,7 @@ jobs: if: github.event.pull_request.merged == true steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run auto changelog uses: actions/github-script@v6 with: diff --git a/.github/workflows/autowiki.yml b/.github/workflows/autowiki.yml index 91ab12cdb19f6..82d164e0c7c2a 100644 --- a/.github/workflows/autowiki.yml +++ b/.github/workflows/autowiki.yml @@ -20,10 +20,10 @@ jobs: echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.secrets_set.outputs.SECRETS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Restore BYOND cache if: steps.secrets_set.outputs.SECRETS_ENABLED - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/BYOND key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }} diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml index f422da8816413..0f6873586253a 100644 --- a/.github/workflows/ci_suite.yml +++ b/.github/workflows/ci_suite.yml @@ -22,44 +22,44 @@ jobs: group: run_linters-${{ github.head_ref || github.run_id }} cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore SpacemanDMM cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/SpacemanDMM key: ${{ runner.os }}-spacemandmm-${{ hashFiles('dependencies.sh') }} restore-keys: | ${{ runner.os }}-spacemandmm- - name: Restore Yarn cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: tgui/.yarn/cache key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - name: Restore Node cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.nvm key: ${{ runner.os }}-node-${{ hashFiles('dependencies.sh') }} restore-keys: | ${{ runner.os }}-node- - name: Restore Bootstrap cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: tools/bootstrap/.cache key: ${{ runner.os }}-bootstrap-${{ hashFiles('tools/requirements.txt') }} restore-keys: | ${{ runner.os }}-bootstrap- - name: Restore Rust cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cargo key: ${{ runner.os }}-rust-${{ hashFiles('tools/ci/ci_dependencies.sh')}} restore-keys: | ${{ runner.os }}-rust- - name: Restore Cutter cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: tools/icon_cutter/cache key: ${{ runner.os }}-cutter-${{ hashFiles('dependencies.sh') }} @@ -124,9 +124,9 @@ jobs: group: compile_all_maps-${{ github.head_ref || github.run_id }} cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore BYOND cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/BYOND key: ${{ runner.os }}-byond @@ -153,7 +153,7 @@ jobs: group: find_all_maps-${{ github.head_ref || github.run_id }} cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Find Maps id: map_finder run: | @@ -220,10 +220,12 @@ jobs: name: Compare Screenshot Tests runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Setup directory + run: mkdir -p artifacts # If we ever add more artifacts, this is going to break, but it'll be obvious. - name: Download screenshot tests - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: artifacts - name: ls -R @@ -244,7 +246,7 @@ jobs: echo ${{ github.event.pull_request.number }} > artifacts/screenshot_comparisons/pull_request_number.txt - name: Upload bad screenshots if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: bad-screenshots path: artifacts/screenshot_comparisons @@ -258,9 +260,9 @@ jobs: group: test_windows-${{ github.head_ref || github.run_id }} cancel-in-progress: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore Yarn cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: tgui/.yarn/cache key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }} diff --git a/.github/workflows/codeowner_reviews.yml b/.github/workflows/codeowner_reviews.yml index ed06f9b8a99d7..cffab706d6100 100644 --- a/.github/workflows/codeowner_reviews.yml +++ b/.github/workflows/codeowner_reviews.yml @@ -12,7 +12,7 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE, so the job can access it - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 #Parse the Codeowner file on non draft PRs - name: CodeOwnersParser diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml index 64d4968ec88aa..e1b8774905f13 100644 --- a/.github/workflows/compile_changelogs.yml +++ b/.github/workflows/compile_changelogs.yml @@ -31,7 +31,7 @@ jobs: sudo apt-get install dos2unix - name: "Checkout" if: steps.value_holder.outputs.ACTIONS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 25 persist-credentials: false diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index c9d30e846f93b..6daec1ded1057 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -8,7 +8,7 @@ jobs: if: ( !contains(github.event.head_commit.message, '[ci skip]') ) runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build and Publish Docker Image to Registry uses: elgohr/Publish-Docker-Github-Action@v5 diff --git a/.github/workflows/gbp.yml b/.github/workflows/gbp.yml index ef782f398143b..221d0462e257a 100644 --- a/.github/workflows/gbp.yml +++ b/.github/workflows/gbp.yml @@ -16,7 +16,7 @@ jobs: echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.value_holder.outputs.ACTIONS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup git if: steps.value_holder.outputs.ACTIONS_ENABLED run: | @@ -24,7 +24,7 @@ jobs: git config --global user.email "<>" - name: Checkout alternate branch if: steps.value_holder.outputs.ACTIONS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: "gbp-balances" # The branch name path: gbp-balances diff --git a/.github/workflows/gbp_collect.yml b/.github/workflows/gbp_collect.yml index a180cb9b8ef4f..4dd327abecd69 100644 --- a/.github/workflows/gbp_collect.yml +++ b/.github/workflows/gbp_collect.yml @@ -18,7 +18,7 @@ jobs: echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.value_holder.outputs.ACTIONS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup git if: steps.value_holder.outputs.ACTIONS_ENABLED run: | @@ -26,7 +26,7 @@ jobs: git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Checkout alternate branch if: steps.value_holder.outputs.ACTIONS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: "gbp-balances" # The branch name path: gbp-balances diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index b8d0dd372f5e8..2ffef72218384 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-22.04 concurrency: gen-docs steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/SpacemanDMM key: ${{ runner.os }}-spacemandmm-${{ secrets.CACHE_PURGE_KEY }} diff --git a/.github/workflows/remove_guide_comments.yml b/.github/workflows/remove_guide_comments.yml index d5d405909e211..e3a4ac3feda06 100644 --- a/.github/workflows/remove_guide_comments.yml +++ b/.github/workflows/remove_guide_comments.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Remove guide comments uses: actions/github-script@v6 with: diff --git a/.github/workflows/rerun_flaky_tests.yml b/.github/workflows/rerun_flaky_tests.yml index 24c3ec95197d8..7f498de144308 100644 --- a/.github/workflows/rerun_flaky_tests.yml +++ b/.github/workflows/rerun_flaky_tests.yml @@ -10,7 +10,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt == 1 }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Rerun flaky tests uses: actions/github-script@v6 with: @@ -22,7 +22,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.run_attempt == 2 }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Report flaky tests uses: actions/github-script@v6 with: diff --git a/.github/workflows/run_integration_tests.yml b/.github/workflows/run_integration_tests.yml index 404119d988060..368430162d394 100644 --- a/.github/workflows/run_integration_tests.yml +++ b/.github/workflows/run_integration_tests.yml @@ -28,9 +28,9 @@ jobs: - 3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore BYOND cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/BYOND key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }} @@ -64,9 +64,9 @@ jobs: bash tools/ci/run_server.sh ${{ inputs.map }} - name: Upload screenshot tests if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: test_artifacts_${{ inputs.map }} + name: test_artifacts_${{ inputs.map }}_${{ inputs.major }}_${{ inputs.minor }} path: data/screenshots_new/ retention-days: 1 - name: Check client Compatibility diff --git a/.github/workflows/show_screenshot_test_results.yml b/.github/workflows/show_screenshot_test_results.yml index f1f1dec2649d0..c61d09fa89057 100644 --- a/.github/workflows/show_screenshot_test_results.yml +++ b/.github/workflows/show_screenshot_test_results.yml @@ -25,7 +25,7 @@ jobs: echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.secrets_set.outputs.SECRETS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare module if: steps.secrets_set.outputs.SECRETS_ENABLED run: | diff --git a/.github/workflows/test_merge_bot.yml b/.github/workflows/test_merge_bot.yml index 4eb62752c065c..c77e507794413 100644 --- a/.github/workflows/test_merge_bot.yml +++ b/.github/workflows/test_merge_bot.yml @@ -23,7 +23,7 @@ jobs: echo "GET_TEST_MERGES_URL=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.secrets_set.outputs.GET_TEST_MERGES_URL - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare module if: steps.secrets_set.outputs.GET_TEST_MERGES_URL run: | diff --git a/.github/workflows/tgs_test.yml b/.github/workflows/tgs_test.yml index 6a9316f493a12..bd538307aa3f3 100644 --- a/.github/workflows/tgs_test.yml +++ b/.github/workflows/tgs_test.yml @@ -62,7 +62,7 @@ jobs: dotnet-version: 8.0.x - name: Checkout Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Test TGS Integration run: dotnet run -c Release --project tools/tgs_test ${{ github.repository }} /tgs_instances/tgstation ${{ env.TGS_API_PORT }} ${{ github.event.pull_request.head.sha || github.sha }} ${{ secrets.GITHUB_TOKEN }} ${{ env.PR_NUMBER }} diff --git a/.github/workflows/update_tgs_dmapi.yml b/.github/workflows/update_tgs_dmapi.yml index aae81f7e0d87a..15d45b7935f05 100644 --- a/.github/workflows/update_tgs_dmapi.yml +++ b/.github/workflows/update_tgs_dmapi.yml @@ -11,7 +11,7 @@ jobs: name: Update the TGS DMAPI steps: - name: Clone - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Branch run: | diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm index 7d422677278e6..faa5cf18ae978 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm @@ -54,6 +54,10 @@ /obj/structure/chair/comfy{ dir = 1 }, +/obj/effect/decal/remains/human/smokey{ + pixel_x = -3; + pixel_y = 9 + }, /turf/open/floor/carpet/blue, /area/ruin/smoking_room/room) "k" = ( @@ -245,11 +249,6 @@ /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) "R" = ( -/obj/effect/spawner/random/entertainment/cigarette_pack, -/obj/effect/decal/cleanable/ash/large{ - pixel_x = -1; - pixel_y = 5 - }, /obj/structure/showcase/machinery/tv/broken, /turf/open/floor/carpet/blue, /area/ruin/smoking_room/room) @@ -279,17 +278,12 @@ /turf/open/floor/stone, /area/ruin/smoking_room/house) "W" = ( -/obj/effect/spawner/random/entertainment/cigarette_pack, /obj/structure/chair/plastic{ dir = 8 }, /obj/effect/spawner/random/entertainment/cigarette_pack, /obj/effect/decal/cleanable/ash/large, /obj/structure/sign/calendar/directional/east, -/obj/effect/decal/remains/human/smokey{ - pixel_x = -3; - pixel_y = 9 - }, /turf/open/floor/carpet/blue, /area/ruin/smoking_room/room) "X" = ( diff --git a/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm b/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm index a636b7220cab5..33c5893bcee4c 100644 --- a/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm +++ b/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm @@ -78,7 +78,7 @@ /turf/open/floor/plating, /area/ruin/space/has_grav/garbagetruck/toystore) "hH" = ( -/obj/structure/spider/solid, +/obj/structure/spider/stickyweb/sealed/tough, /obj/item/book/manual/wiki/cytology, /obj/effect/decal/cleanable/plastic, /turf/open/floor/plating, @@ -122,7 +122,7 @@ /area/ruin/space/has_grav/garbagetruck/toystore) "lm" = ( /obj/structure/spider/stickyweb, -/obj/structure/spider/sticky, +/obj/structure/spider/stickyweb/very_sticky, /turf/open/floor/plating, /area/ruin/space/has_grav/garbagetruck/toystore) "mf" = ( @@ -182,7 +182,7 @@ /turf/open/floor/plating, /area/ruin/space/has_grav/garbagetruck/toystore) "qX" = ( -/obj/structure/spider/sticky, +/obj/structure/spider/stickyweb/very_sticky, /obj/item/food/badrecipe/moldy, /obj/structure/spider/stickyweb, /obj/item/food/spidereggs{ @@ -255,7 +255,7 @@ /turf/open/floor/plating, /area/ruin/space/has_grav/garbagetruck/toystore) "ts" = ( -/obj/structure/spider/solid, +/obj/structure/spider/stickyweb/sealed/tough, /obj/structure/spider/stickyweb, /obj/structure/closet/crate/trashcart/filled, /turf/open/floor/plating, @@ -841,7 +841,7 @@ /turf/open/floor/plating, /area/ruin/space/has_grav/garbagetruck/toystore) "XI" = ( -/obj/structure/spider/solid, +/obj/structure/spider/stickyweb/sealed/tough, /obj/item/food/badrecipe/moldy/bacteria, /turf/open/floor/plating, /area/ruin/space/has_grav/garbagetruck/toystore) diff --git a/_maps/RandomRuins/SpaceRuins/meatderelict.dmm b/_maps/RandomRuins/SpaceRuins/meatderelict.dmm index 0c4e9cd740b37..3e4bece11e8e5 100644 --- a/_maps/RandomRuins/SpaceRuins/meatderelict.dmm +++ b/_maps/RandomRuins/SpaceRuins/meatderelict.dmm @@ -733,7 +733,7 @@ /turf/open/indestructible/white, /area/ruin/space/has_grav/powered/biooutpost) "oQ" = ( -/obj/machinery/puzzle_button/meatderelict{ +/obj/machinery/puzzle/button/meatderelict{ pixel_y = 32; queue_size = 4 }, @@ -1669,7 +1669,7 @@ "FI" = ( /obj/item/instrument/piano_synth/headphones, /obj/structure/table, -/obj/machinery/puzzle_button/directional/north{ +/obj/machinery/puzzle/button/directional/north{ used = 1 }, /obj/effect/turf_decal/tile/neutral/opposingcorners, @@ -1820,7 +1820,7 @@ /turf/open/floor/iron/dark/textured_large, /area/ruin/space/has_grav/powered/biooutpost/vault) "Jt" = ( -/obj/machinery/puzzle_button/directional/north{ +/obj/machinery/puzzle/button/directional/north{ id = "md_tosci"; name = "shield power panel" }, @@ -1889,7 +1889,7 @@ /turf/open/indestructible/white, /area/ruin/space/has_grav/powered/biooutpost) "KC" = ( -/obj/machinery/puzzle_button/directional/north{ +/obj/machinery/puzzle/button/directional/north{ id = "md_toeng"; queue_size = 4 }, @@ -2138,7 +2138,7 @@ id = "md_armory" }, /obj/effect/turf_decal/stripes/full, -/obj/machinery/puzzle_keycardpad/directional/south{ +/obj/machinery/puzzle/keycardpad/directional/south{ id = "md_armory"; name = "armory authentication pad"; queue_size = 5 diff --git a/_maps/RandomZLevels/museum.dmm b/_maps/RandomZLevels/museum.dmm index f40ecc09015c6..64fd580b80f78 100644 --- a/_maps/RandomZLevels/museum.dmm +++ b/_maps/RandomZLevels/museum.dmm @@ -9,11 +9,18 @@ /obj/machinery/computer/old{ name = "replica computer"; dir = 8; - icon_keyboard = "rd_key"; - icon_screen = "rdcomp" + icon_screen = "rdcomp"; + icon_keyboard = "rd_key" }, /turf/open/floor/iron/smooth_large, /area/awaymission/museum) +"ac" = ( +/obj/structure/sign/poster/contraband/fake_bombable/directional/west, +/obj/structure/closet/crate/bin, +/obj/machinery/light/warm/directional/south, +/obj/effect/decal/cleanable/dirt/dust, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "ai" = ( /obj/structure/railing{ dir = 8 @@ -59,6 +66,11 @@ }, /turf/open/indestructible/plating, /area/awaymission/museum) +"ax" = ( +/obj/structure/sink/directional/north, +/obj/structure/mirror/directional/south, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "az" = ( /obj/structure/table/reinforced, /obj/effect/spawner/random/entertainment/musical_instrument, @@ -76,6 +88,22 @@ /obj/structure/closet/crate/preopen, /turf/open/indestructible/plating, /area/awaymission/museum) +"aO" = ( +/obj/machinery/button/door/directional/north{ + name = "Lock Control"; + id = "museum_toilet_wontwork" + }, +/obj/effect/mob_spawn/corpse/human/skeleton/museum_chef, +/obj/structure/toilet/museum{ + dir = 4 + }, +/obj/item/keycard/cafeteria, +/obj/item/paper/fluff/museum/chefs_ultimatum, +/obj/machinery/light/small/dim/directional/west, +/obj/effect/decal/cleanable/dirt/dust, +/obj/effect/decal/cleanable/cobweb, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "aR" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/effect/turf_decal/siding/dark_blue{ @@ -147,8 +175,8 @@ }, /turf/open/mirage{ dir = 8; - target_turf_x = 11; - range = 1 + range = 1; + target_turf_x = 11 }, /area/awaymission/museum) "bC" = ( @@ -177,8 +205,8 @@ /area/awaymission/museum) "bJ" = ( /mob/living/basic/statue/mannequin{ - dir = 8; - name = "Dale Knox" + name = "Dale Knox"; + dir = 8 }, /obj/effect/turf_decal/tile/blue/opposingcorners, /turf/open/floor/holofloor/white, @@ -229,6 +257,14 @@ /obj/item/storage/box/stickers/googly, /turf/open/floor/iron/dark, /area/awaymission/museum) +"ci" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/effect/decal/cleanable/dirt/dust, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "cm" = ( /obj/machinery/door/airlock/grunge, /obj/structure/barricade/wooden/crude, @@ -255,8 +291,8 @@ /turf/open/floor/iron/smooth_half, /area/awaymission/museum) "ct" = ( -/obj/structure/chair/comfy, /mob/living/basic/mothroach, +/obj/structure/chair/comfy, /obj/effect/mapping_helpers/mob_buckler, /obj/machinery/light/dim/directional/north, /turf/open/floor/iron/dark/textured_large, @@ -313,8 +349,8 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/item/toy/balloon/corgi, /obj/machinery/status_display/random_message{ - firstline_to_secondline = list("NO" = "LITTERING","YOU ARE" = "BEING WATCHED", "DO NOT TOUCH" = "THE EXHIBITS"); - pixel_x = 32 + pixel_x = 32; + firstline_to_secondline = list(NO="LITTERING", "YOU ARE"="BEING WATCHED", "DO NOT TOUCH"="THE EXHIBITS") }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -328,9 +364,9 @@ "cS" = ( /obj/effect/step_trigger/thrower{ direction = 1; - facedir = 1; + mobs_only = 1; tiles = 10; - mobs_only = 1 + facedir = 1 }, /obj/machinery/light/floor, /turf/open/floor/iron, @@ -343,6 +379,14 @@ /obj/structure/plaque/static_plaque/golden/commission/dream, /turf/closed/indestructible/reinforced, /area/awaymission/museum) +"cY" = ( +/obj/structure/fluff/fake_camera{ + dir = 5 + }, +/obj/machinery/light/warm/directional/east, +/obj/structure/fluff/fake_scrubber, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "cZ" = ( /obj/structure/fluff/divine/nexus, /turf/open/floor/cult, @@ -398,9 +442,9 @@ /area/awaymission/museum) "dK" = ( /turf/open/mirage{ + dir = 1; range = 2; - target_turf_y = -4; - dir = 1 + target_turf_y = -4 }, /area/awaymission/museum) "dL" = ( @@ -434,8 +478,8 @@ /area/awaymission/museum) "dY" = ( /mob/living/basic/statue/mannequin{ - hat = /obj/item/clothing/head/helmet/space; - dir = 1 + dir = 1; + hat = /obj/item/clothing/head/helmet/space }, /turf/open/floor/holofloor/hyperspace/ns, /area/awaymission/museum) @@ -462,7 +506,7 @@ /obj/machinery/door/poddoor/shutters/indestructible{ id = "museum_secret" }, -/obj/machinery/puzzle_keycardpad/directional/east{ +/obj/machinery/puzzle/keycardpad/directional/east{ id = "museum_secret" }, /turf/open/floor/iron/dark, @@ -521,10 +565,10 @@ /turf/open/floor/grass, /area/awaymission/museum) "ex" = ( +/mob/living/basic/mothroach, /obj/structure/chair/stool/bar/directional/west{ can_buckle = 1 }, -/mob/living/basic/mothroach, /obj/effect/mapping_helpers/mob_buckler, /turf/open/floor/wood/large, /area/awaymission/museum) @@ -596,6 +640,15 @@ /obj/structure/plaque/static_plaque/golden/commission/omega, /turf/closed/indestructible/reinforced, /area/awaymission/museum) +"eW" = ( +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/effect/turf_decal/siding/dark_blue, +/obj/structure/fluff/wallsign/directional/north{ + name = "Restrooms"; + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "eX" = ( /obj/effect/turf_decal/siding/wideplating{ dir = 4 @@ -605,11 +658,11 @@ }, /area/awaymission/museum) "fa" = ( -/obj/effect/turf_decal/sand/plating, /mob/living/basic/statue/mannequin{ held_item = /obj/item/pickaxe; hat = /obj/item/clothing/suit/hooded/explorer }, +/obj/effect/turf_decal/sand/plating, /obj/effect/turf_decal/mining, /turf/open/indestructible/plating, /area/awaymission/museum) @@ -627,6 +680,13 @@ /obj/item/kitchen/fork, /turf/open/floor/wood/tile, /area/awaymission/museum) +"fl" = ( +/obj/machinery/status_display/random_message{ + pixel_x = -32; + firstline_to_secondline = list(CAFETERIA="YUMMY", CAFETERIA="HOTDOGS", ENJOY="YOUR MEAL") + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "fn" = ( /turf/open/floor/holofloor/hyperspace/ns, /area/awaymission/museum) @@ -706,6 +766,11 @@ }, /turf/open/floor/iron, /area/awaymission/museum) +"gg" = ( +/obj/effect/decal/cleanable/dirt/dust, +/obj/machinery/shower/directional/east, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "gj" = ( /obj/machinery/door/poddoor/shutters/preopen{ dir = 8 @@ -751,6 +816,10 @@ /obj/machinery/light/small/dim/directional/south, /turf/open/indestructible/plating, /area/awaymission/museum) +"gE" = ( +/obj/item/clothing/suit/caution, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "gG" = ( /obj/effect/turf_decal/siding/dark_blue/corner{ dir = 4 @@ -814,8 +883,8 @@ "hk" = ( /obj/effect/smooths_with_walls, /turf/open/mirage{ - target_turf_y = -29; - dir = 1 + dir = 1; + target_turf_y = -29 }, /area/awaymission/museum) "hl" = ( @@ -827,6 +896,12 @@ }, /turf/open/indestructible/plating, /area/awaymission/museum) +"hm" = ( +/obj/structure/table, +/obj/effect/spawner/random/food_or_drink/condiment, +/obj/machinery/light/floor, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "hp" = ( /mob/living/basic/mothroach/bar, /turf/open/floor/wood/tile, @@ -874,10 +949,18 @@ "hM" = ( /turf/closed/wall/rock/porous, /area/awaymission/museum) +"hS" = ( +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/effect/decal/cleanable/dirt/dust, +/obj/effect/turf_decal/siding/dark_blue{ + dir = 5 + }, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "hT" = ( /mob/living/basic/statue/mannequin{ - dir = 8; name = "Dale Knox"; + dir = 8; held_item = /obj/item/circuitboard }, /obj/structure/sign/flag/nanotrasen/directional/south, @@ -892,8 +975,8 @@ /area/awaymission/museum) "hX" = ( /obj/item/circuitboard{ - icon_state = "flopdrive"; - name = "microprocessor" + name = "microprocessor"; + icon_state = "flopdrive" }, /obj/structure/table/reinforced, /turf/open/floor/circuit/green, @@ -983,6 +1066,15 @@ /obj/machinery/light/floor, /turf/open/floor/iron/dark, /area/awaymission/museum) +"iJ" = ( +/obj/effect/decal/cleanable/dirt/dust, +/obj/structure/toilet/museum{ + dir = 1 + }, +/obj/structure/curtain, +/obj/structure/broken_flooring/plating, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "iK" = ( /obj/effect/turf_decal/sand/plating, /obj/effect/turf_decal/siding{ @@ -1031,9 +1123,9 @@ }, /obj/effect/step_trigger/thrower{ direction = 1; - facedir = 1; + mobs_only = 1; tiles = 10; - mobs_only = 1 + facedir = 1 }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -1067,10 +1159,20 @@ }, /turf/open/floor/cult, /area/awaymission/museum) +"ju" = ( +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/effect/turf_decal/siding/dark_blue{ + dir = 6 + }, +/obj/structure/fluff/wallsign/directional/north{ + name = "Restrooms" + }, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "jy" = ( /mob/living/basic/statue/mannequin{ - dir = 8; name = "Dale Knox"; + dir = 8; held_item = /obj/item/circuitboard }, /obj/effect/turf_decal/stripes{ @@ -1082,6 +1184,14 @@ /obj/effect/spawner/structure/window, /turf/open/indestructible/plating, /area/awaymission/museum) +"jC" = ( +/obj/effect/puzzle_poddoor_open{ + icon = 'icons/effects/mapping_helpers.dmi'; + id = "museum_right_wing"; + queue_id = "museum_right_wing" + }, +/turf/closed/indestructible/reinforced, +/area/awaymission/museum) "jF" = ( /obj/machinery/suit_storage_unit/open, /obj/effect/turf_decal/box, @@ -1110,6 +1220,18 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/dark, /area/awaymission/museum) +"jO" = ( +/obj/structure/toilet/museum, +/obj/structure/sign/poster/contraband/fake_bombable/directional/north, +/obj/machinery/button/door/directional/east{ + name = "Lock Control"; + id = "museum_toilet6"; + specialfunctions = 4; + normaldoorcontrol = 1 + }, +/obj/machinery/light/small/dim/directional/north, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "jP" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/effect/turf_decal/siding/dark_blue/corner{ @@ -1120,13 +1242,16 @@ dir = 9 }, /obj/item/reagent_containers/cup/glass/coffee, +/obj/item/paper/fluff/scrambled_pass{ + puzzle_id = "museum_r_wing_puzzle" + }, /turf/open/floor/iron/dark, /area/awaymission/museum) "jU" = ( /obj/effect/landmark/transport/nav_beacon/tram/platform{ + name = "Exhibit Loading Bay"; specific_transport_id = "museum_cargo"; - platform_code = 2; - name = "Exhibit Loading Bay" + platform_code = 2 }, /turf/open/chasm/true/no_smooth, /area/awaymission/museum) @@ -1193,6 +1318,12 @@ }, /turf/open/floor/iron, /area/awaymission/museum) +"kx" = ( +/obj/machinery/door/airlock/public{ + name = "Restrooms" + }, +/turf/open/floor/iron, +/area/awaymission/museum) "kA" = ( /obj/machinery/conveyor{ dir = 1 @@ -1200,6 +1331,16 @@ /obj/item/vending_refill/wardrobe/coroner_wardrobe, /turf/open/indestructible/plating, /area/awaymission/museum) +"kL" = ( +/obj/machinery/door/poddoor/shutters/window/indestructible{ + dir = 8; + id = "museum_cafeteria" + }, +/obj/machinery/puzzle/keycardpad/directional/south{ + id = "museum_cafeteria" + }, +/turf/open/floor/iron, +/area/awaymission/museum) "kO" = ( /obj/structure/railing{ dir = 8 @@ -1227,6 +1368,19 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/indestructible/plating, /area/awaymission/museum) +"kZ" = ( +/obj/structure/toilet/museum{ + dir = 4 + }, +/obj/machinery/button/door/directional/north{ + name = "Lock Control"; + id = "museum_toilet5"; + specialfunctions = 4; + normaldoorcontrol = 1 + }, +/obj/machinery/light/small/dim/directional/west, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "la" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -1324,11 +1478,11 @@ /turf/open/floor/iron, /area/awaymission/museum) "lT" = ( -/obj/structure/chair/office{ - dir = 1 - }, /mob/living/basic/statue/mannequin{ - hat = /obj/item/clothing/suit/toggle/labcoat/science; + dir = 1; + hat = /obj/item/clothing/suit/toggle/labcoat/science + }, +/obj/structure/chair/office{ dir = 1 }, /obj/machinery/light/floor, @@ -1345,9 +1499,9 @@ specific_transport_id = "museum_cargo" }, /obj/effect/landmark/transport/nav_beacon/tram/platform{ + name = "Internal Loading Bay"; specific_transport_id = "museum_cargo"; - platform_code = 1; - name = "Internal Loading Bay" + platform_code = 1 }, /turf/open/chasm/true/no_smooth, /area/awaymission/museum) @@ -1379,6 +1533,9 @@ }, /turf/open/chasm/true/no_smooth, /area/awaymission/museum) +"ms" = ( +/turf/open/floor/iron, +/area/awaymission/museum/cafeteria) "mA" = ( /obj/structure/window/reinforced/spawner/directional/west, /turf/open/floor/holofloor/beach/water, @@ -1403,9 +1560,9 @@ /area/awaymission/museum) "mS" = ( /mob/living/basic/statue/mannequin{ + dir = 1; held_item = /obj/item/wrench; - hat = /obj/item/clothing/head/utility/hardhat; - dir = 1 + hat = /obj/item/clothing/head/utility/hardhat }, /obj/effect/decal/cleanable/dirt/dust, /obj/effect/decal/cleanable/blood/gibs, @@ -1436,8 +1593,8 @@ /obj/structure/transport/linear/tram/slow, /obj/structure/tram, /obj/machinery/transport/tram_controller{ - configured_transport_id = "museum_cargo"; - cover_locked = 0 + cover_locked = 0; + configured_transport_id = "museum_cargo" }, /turf/open/chasm/true/no_smooth, /area/awaymission/museum) @@ -1477,8 +1634,8 @@ /turf/open/floor/iron/white, /area/awaymission/museum) "nu" = ( -/obj/effect/decal/cleanable/glass/titanium, /mob/living/basic/mouse/rat, +/obj/effect/decal/cleanable/glass/titanium, /turf/open/floor/iron/white, /area/awaymission/museum) "nv" = ( @@ -1510,9 +1667,9 @@ "nz" = ( /obj/structure/lattice/catwalk/mining, /obj/structure/fluff{ + name = "old plasma extractor"; icon = 'icons/mob/simple/hivebot.dmi'; - icon_state = "fab_robot"; - name = "old plasma extractor" + icon_state = "fab_robot" }, /turf/open/lava/plasma/mafia, /area/awaymission/museum) @@ -1537,9 +1694,9 @@ }, /obj/effect/step_trigger/thrower{ direction = 1; - facedir = 1; + mobs_only = 1; tiles = 10; - mobs_only = 1 + facedir = 1 }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -1588,6 +1745,16 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"nY" = ( +/obj/structure/sign/poster/official/no_erp/directional/north, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) +"nZ" = ( +/obj/structure/chair/sofa/bench/left{ + dir = 1 + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "od" = ( /obj/structure/fluff/fake_vent, /turf/open/floor/iron, @@ -1648,11 +1815,31 @@ /obj/structure/flora/coconuts, /turf/open/misc/beach/sand, /area/awaymission/museum/mothroachvoid) +"oD" = ( +/obj/structure/toilet/museum{ + dir = 4 + }, +/obj/machinery/button/door/directional/north{ + name = "Lock Control"; + id = "museum_toilet4"; + specialfunctions = 4; + normaldoorcontrol = 1 + }, +/obj/machinery/light/small/dim/directional/west, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "oI" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/floor, /turf/open/floor/iron, /area/awaymission/museum) +"oP" = ( +/obj/effect/puzzle_poddoor_open{ + id = "museum_cafeteria"; + queue_id = "museum_cafeteria" + }, +/turf/closed/indestructible/reinforced, +/area/awaymission/museum) "oQ" = ( /turf/open/floor/holofloor/beach/coast{ dir = 1 @@ -1694,8 +1881,8 @@ /area/awaymission/museum) "pi" = ( /mob/living/basic/statue/mannequin{ - hat = /obj/item/clothing/head/costume/nursehat; - held_item = /obj/item/clothing/neck/stethoscope + held_item = /obj/item/clothing/neck/stethoscope; + hat = /obj/item/clothing/head/costume/nursehat }, /obj/effect/turf_decal/tile/blue/opposingcorners, /turf/open/floor/holofloor/white, @@ -1729,6 +1916,13 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/white, /area/awaymission/museum) +"pz" = ( +/obj/machinery/door/poddoor/shutters/window/indestructible{ + dir = 8; + id = "museum_cafeteria" + }, +/turf/open/floor/iron, +/area/awaymission/museum) "pD" = ( /obj/structure/broken_flooring/corner/always_floorplane/directional/west, /obj/effect/decal/cleanable/dirt/dust, @@ -1758,6 +1952,17 @@ }, /turf/open/indestructible/plating, /area/awaymission/museum) +"pN" = ( +/obj/effect/decal/cleanable/dirt/dust, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/effect/turf_decal/siding/dark_blue{ + dir = 6 + }, +/obj/structure/fluff/fake_camera{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "pX" = ( /obj/structure/rack, /obj/effect/spawner/random/maintenance/two, @@ -1773,6 +1978,10 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron, /area/awaymission/museum) +"ql" = ( +/obj/structure/chair/sofa/bench, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "qo" = ( /obj/effect/oneway{ dir = 8 @@ -1784,15 +1993,15 @@ /area/awaymission/museum) "qt" = ( /obj/effect/spawner/random/food_or_drink/booze{ - loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer = 10, /obj/item/reagent_containers/cup/glass/bottle/ale = 10, /obj/item/reagent_containers/cup/glass/bottle/beer/light = 5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor = 5, /obj/item/reagent_containers/cup/glass/bottle/whiskey = 5, /obj/item/reagent_containers/cup/glass/bottle/gin = 5, /obj/item/reagent_containers/cup/glass/bottle/vodka = 5, /obj/item/reagent_containers/cup/glass/bottle/tequila = 5, /obj/item/reagent_containers/cup/glass/bottle/rum = 5, /obj/item/reagent_containers/cup/glass/bottle/vermouth = 5, /obj/item/reagent_containers/cup/glass/bottle/cognac = 5, /obj/item/reagent_containers/cup/glass/bottle/wine = 5, /obj/item/reagent_containers/cup/glass/bottle/kahlua = 5, /obj/item/reagent_containers/cup/glass/bottle/amaretto = 5, /obj/item/reagent_containers/cup/glass/bottle/hcider = 5, /obj/item/reagent_containers/cup/glass/bottle/absinthe = 5, /obj/item/reagent_containers/cup/glass/bottle/sake = 5, /obj/item/reagent_containers/cup/glass/bottle/grappa = 5, /obj/item/reagent_containers/cup/glass/bottle/applejack = 5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic = 5, /obj/item/reagent_containers/cup/bottle/ethanol = 2, /obj/item/reagent_containers/cup/glass/bottle/fernet = 2, /obj/item/reagent_containers/cup/glass/bottle/champagne = 2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium = 2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager = 2, /obj/item/reagent_containers/cup/glass/bottle/patron = 1, /obj/item/reagent_containers/cup/glass/bottle/kong = 1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine = 1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka = 1, /obj/item/reagent_containers/cup/glass/bottle/trappist = 1); + loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer=10, /obj/item/reagent_containers/cup/glass/bottle/ale=10, /obj/item/reagent_containers/cup/glass/bottle/beer/light=5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor=5, /obj/item/reagent_containers/cup/glass/bottle/whiskey=5, /obj/item/reagent_containers/cup/glass/bottle/gin=5, /obj/item/reagent_containers/cup/glass/bottle/vodka=5, /obj/item/reagent_containers/cup/glass/bottle/tequila=5, /obj/item/reagent_containers/cup/glass/bottle/rum=5, /obj/item/reagent_containers/cup/glass/bottle/vermouth=5, /obj/item/reagent_containers/cup/glass/bottle/cognac=5, /obj/item/reagent_containers/cup/glass/bottle/wine=5, /obj/item/reagent_containers/cup/glass/bottle/kahlua=5, /obj/item/reagent_containers/cup/glass/bottle/amaretto=5, /obj/item/reagent_containers/cup/glass/bottle/hcider=5, /obj/item/reagent_containers/cup/glass/bottle/absinthe=5, /obj/item/reagent_containers/cup/glass/bottle/sake=5, /obj/item/reagent_containers/cup/glass/bottle/grappa=5, /obj/item/reagent_containers/cup/glass/bottle/applejack=5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic=5, /obj/item/reagent_containers/cup/bottle/ethanol=2, /obj/item/reagent_containers/cup/glass/bottle/fernet=2, /obj/item/reagent_containers/cup/glass/bottle/champagne=2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium=2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager=2, /obj/item/reagent_containers/cup/glass/bottle/patron=1, /obj/item/reagent_containers/cup/glass/bottle/kong=1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine=1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka=1, /obj/item/reagent_containers/cup/glass/bottle/trappist=1); spawn_random_offset = 2 }, /turf/open/floor/carpet/cyan, /area/awaymission/museum/mothroachvoid) "qw" = ( /obj/item/flashlight/flare{ - start_on = 1; - icon_state = "flare-on" + icon_state = "flare-on"; + start_on = 1 }, /obj/structure/table, /obj/effect/decal/cleanable/dirt/dust, @@ -1822,6 +2031,10 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/engine, /area/awaymission/museum) +"qK" = ( +/obj/machinery/vending/hotdog/museum, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "qL" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/effect/turf_decal/siding/dark_blue{ @@ -1879,8 +2092,8 @@ /area/awaymission/museum) "qY" = ( /obj/machinery/power/shuttle_engine/heater{ - dir = 4; - opacity = 1 + opacity = 1; + dir = 4 }, /obj/structure/window/reinforced/spawner/directional/west, /turf/open/indestructible/plating, @@ -1909,22 +2122,29 @@ /turf/open/floor/carpet/executive, /area/awaymission/museum) "rq" = ( +/mob/living/basic/statue/mannequin/suspicious, /obj/effect/decal/cleanable/blood/old, /obj/effect/gibspawner/human, /obj/effect/gibspawner/human, /obj/effect/gibspawner/human, /obj/effect/gibspawner/human, -/mob/living/basic/statue/mannequin/suspicious, /turf/open/floor/iron, /area/awaymission/museum) "rr" = ( /turf/open/floor/wood/tile, /area/awaymission/museum) +"ry" = ( +/obj/machinery/door/airlock{ + name = "Restroom Cabin 6"; + id_tag = "museum_toilet6" + }, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "rA" = ( +/mob/living/basic/mothroach, /obj/structure/chair/comfy/beige{ dir = 8 }, -/mob/living/basic/mothroach, /obj/effect/mapping_helpers/mob_buckler, /turf/open/floor/wood/large, /area/awaymission/museum) @@ -1964,13 +2184,17 @@ dir = 1 }, /obj/structure/railing/corner/end, +/obj/structure/fluff/wallsign/directional/south{ + name = "Cafeteria"; + dir = 4 + }, /turf/open/floor/iron/dark, /area/awaymission/museum) "rU" = ( /mob/living/basic/statue/mannequin{ + name = "Michael Trasen"; dir = 4; - held_item = /obj/item/wrench; - name = "Michael Trasen" + held_item = /obj/item/wrench }, /obj/structure/sign/flag/nanotrasen/directional/south, /obj/machinery/light/small/dim/directional/west, @@ -1997,9 +2221,9 @@ /turf/open/floor/engine, /area/awaymission/museum) "rY" = ( +/mob/living/basic/mouse/rat, /obj/machinery/light/small/broken/directional/north, /obj/effect/decal/cleanable/dirt/dust, -/mob/living/basic/mouse/rat, /turf/open/floor/iron/dark/textured_large, /area/awaymission/museum) "sd" = ( @@ -2110,13 +2334,20 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/dark, /area/awaymission/museum) +"ta" = ( +/obj/machinery/door/airlock{ + name = "Restroom Cabin 5"; + id_tag = "museum_toilet5" + }, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "tc" = ( +/mob/living/basic/mothroach, /obj/structure/lattice/catwalk/mining, /obj/structure/railing{ dir = 6 }, /obj/structure/chair, -/mob/living/basic/mothroach, /obj/effect/mapping_helpers/mob_buckler, /turf/open/misc/grass, /area/awaymission/museum/mothroachvoid) @@ -2136,15 +2367,15 @@ /turf/open/floor/holofloor/asteroid, /area/awaymission/museum) "tj" = ( +/mob/living/basic/statue/mannequin{ + dir = 1; + held_item = /obj/item/wrench; + hat = /obj/item/clothing/head/utility/hardhat + }, /obj/effect/turf_decal/sand/plating, /obj/effect/turf_decal/stripes/asteroid/line{ dir = 1 }, -/mob/living/basic/statue/mannequin{ - dir = 1; - hat = /obj/item/clothing/head/utility/hardhat; - held_item = /obj/item/wrench - }, /turf/open/indestructible/plating, /area/awaymission/museum) "tk" = ( @@ -2185,7 +2416,7 @@ "tx" = ( /obj/structure/table/wood, /obj/effect/spawner/random/food_or_drink/booze{ - loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer = 10, /obj/item/reagent_containers/cup/glass/bottle/ale = 10, /obj/item/reagent_containers/cup/glass/bottle/beer/light = 5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor = 5, /obj/item/reagent_containers/cup/glass/bottle/whiskey = 5, /obj/item/reagent_containers/cup/glass/bottle/gin = 5, /obj/item/reagent_containers/cup/glass/bottle/vodka = 5, /obj/item/reagent_containers/cup/glass/bottle/tequila = 5, /obj/item/reagent_containers/cup/glass/bottle/rum = 5, /obj/item/reagent_containers/cup/glass/bottle/vermouth = 5, /obj/item/reagent_containers/cup/glass/bottle/cognac = 5, /obj/item/reagent_containers/cup/glass/bottle/wine = 5, /obj/item/reagent_containers/cup/glass/bottle/kahlua = 5, /obj/item/reagent_containers/cup/glass/bottle/amaretto = 5, /obj/item/reagent_containers/cup/glass/bottle/hcider = 5, /obj/item/reagent_containers/cup/glass/bottle/absinthe = 5, /obj/item/reagent_containers/cup/glass/bottle/sake = 5, /obj/item/reagent_containers/cup/glass/bottle/grappa = 5, /obj/item/reagent_containers/cup/glass/bottle/applejack = 5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic = 5, /obj/item/reagent_containers/cup/bottle/ethanol = 2, /obj/item/reagent_containers/cup/glass/bottle/fernet = 2, /obj/item/reagent_containers/cup/glass/bottle/champagne = 2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium = 2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager = 2, /obj/item/reagent_containers/cup/glass/bottle/patron = 1, /obj/item/reagent_containers/cup/glass/bottle/kong = 1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine = 1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka = 1, /obj/item/reagent_containers/cup/glass/bottle/trappist = 1); + loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer=10, /obj/item/reagent_containers/cup/glass/bottle/ale=10, /obj/item/reagent_containers/cup/glass/bottle/beer/light=5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor=5, /obj/item/reagent_containers/cup/glass/bottle/whiskey=5, /obj/item/reagent_containers/cup/glass/bottle/gin=5, /obj/item/reagent_containers/cup/glass/bottle/vodka=5, /obj/item/reagent_containers/cup/glass/bottle/tequila=5, /obj/item/reagent_containers/cup/glass/bottle/rum=5, /obj/item/reagent_containers/cup/glass/bottle/vermouth=5, /obj/item/reagent_containers/cup/glass/bottle/cognac=5, /obj/item/reagent_containers/cup/glass/bottle/wine=5, /obj/item/reagent_containers/cup/glass/bottle/kahlua=5, /obj/item/reagent_containers/cup/glass/bottle/amaretto=5, /obj/item/reagent_containers/cup/glass/bottle/hcider=5, /obj/item/reagent_containers/cup/glass/bottle/absinthe=5, /obj/item/reagent_containers/cup/glass/bottle/sake=5, /obj/item/reagent_containers/cup/glass/bottle/grappa=5, /obj/item/reagent_containers/cup/glass/bottle/applejack=5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic=5, /obj/item/reagent_containers/cup/bottle/ethanol=2, /obj/item/reagent_containers/cup/glass/bottle/fernet=2, /obj/item/reagent_containers/cup/glass/bottle/champagne=2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium=2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager=2, /obj/item/reagent_containers/cup/glass/bottle/patron=1, /obj/item/reagent_containers/cup/glass/bottle/kong=1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine=1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka=1, /obj/item/reagent_containers/cup/glass/bottle/trappist=1); spawn_random_offset = 2 }, /turf/open/floor/wood/tile, @@ -2193,9 +2424,9 @@ "ty" = ( /obj/structure/table, /obj/item/clothing/gloves/color/yellow{ - siemens_coefficient = 10; name = "fake stungloves"; - desc = "A crude replica of stungloves. Essentially gloves wrapped with wire. Extremely unsafe." + desc = "A crude replica of stungloves. Essentially gloves wrapped with wire. Extremely unsafe."; + siemens_coefficient = 10 }, /obj/machinery/light/floor, /turf/open/floor/iron, @@ -2293,6 +2524,13 @@ dir = 2 }, /area/awaymission/museum/mothroachvoid) +"um" = ( +/obj/structure/window/reinforced/spawner/directional/east, +/obj/structure/sign/warning/fire/directional/west, +/obj/structure/tank_holder/extinguisher/anti, +/obj/effect/decal/cleanable/dirt/dust, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "up" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -2302,6 +2540,21 @@ }, /turf/open/floor/iron/showroomfloor, /area/awaymission/museum) +"us" = ( +/obj/machinery/door/airlock{ + name = "Restroom Cabin 2"; + id_tag = "museum_toilet2" + }, +/turf/open/floor/iron, +/area/awaymission/museum) +"ut" = ( +/obj/machinery/light/very_dim/directional/north, +/obj/effect/decal/cleanable/dirt/dust, +/obj/structure/fluff/fake_vent, +/obj/structure/mop_bucket, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "uu" = ( /obj/structure/rack, /obj/effect/spawner/random/engineering/material, @@ -2315,6 +2568,10 @@ }, /turf/open/floor/grass, /area/awaymission/museum) +"uH" = ( +/obj/structure/sign/clock/directional/north, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "uM" = ( /obj/structure/fluff/fake_camera{ dir = 10 @@ -2345,8 +2602,8 @@ "vc" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/structure/sign/painting/large{ - persistence_id = "museumgate_big"; - dir = 1 + dir = 1; + persistence_id = "museumgate_big" }, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/dark, @@ -2418,6 +2675,11 @@ /obj/structure/filingcabinet, /turf/open/floor/iron, /area/awaymission/museum) +"vG" = ( +/turf/closed/indestructible/fakedoor{ + name = "Kitchen" + }, +/area/awaymission/museum/cafeteria) "vM" = ( /mob/living/basic/statue/mannequin{ dir = 1; @@ -2448,6 +2710,16 @@ /obj/effect/turf_decal/siding/dark_blue, /turf/open/floor/mineral/titanium/blue, /area/awaymission/museum) +"wg" = ( +/obj/machinery/door/airlock{ + name = "Restroom Cabin 3" + }, +/obj/effect/mapping_helpers/airlock/locked, +/obj/effect/mapping_helpers/airlock_note_placer{ + note_info = "I'VE WORKED HERE FOR 7 YEARS! JUST TO BE REPLACED BY A VENDING MACHINE?! YOU KNOW WHAT? FUCK YOU, I'VE LOCKED DOWN THE CAFETERIA AND WON'T BE COMING OUT 'TILL YOU GIVE ME MY JOB BACK!" + }, +/turf/open/floor/iron, +/area/awaymission/museum) "wh" = ( /obj/structure/broken_flooring/corner/always_floorplane/directional/west, /obj/structure/lattice, @@ -2456,7 +2728,7 @@ "wi" = ( /obj/machinery/door/poddoor/shutters/indestructible{ dir = 8; - id = "nothing" + id = "museum_secret" }, /turf/open/floor/iron, /area/awaymission/museum) @@ -2526,8 +2798,8 @@ "wN" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/structure/sign/painting/large{ - persistence_id = "museumgate_big"; - dir = 1 + dir = 1; + persistence_id = "museumgate_big" }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -2535,6 +2807,13 @@ /obj/item/kirbyplants/random, /turf/open/floor/wood/large, /area/awaymission/museum) +"wV" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "wZ" = ( /turf/open/misc/beach/coast{ dir = 1 @@ -2558,8 +2837,8 @@ /obj/structure/table/reinforced, /obj/structure/window/spawner/directional/west, /obj/effect/spawner/random/bureaucracy/paper{ - spawn_random_offset = 7; - spawn_loot_count = 8 + spawn_loot_count = 8; + spawn_random_offset = 7 }, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/white, @@ -2575,8 +2854,8 @@ "xg" = ( /obj/structure/table/reinforced, /obj/item/circuitboard{ - icon_state = "bluespacearray"; - name = "fancy replica tech" + name = "fancy replica tech"; + icon_state = "bluespacearray" }, /turf/open/floor/iron/smooth_large, /area/awaymission/museum) @@ -2599,6 +2878,10 @@ }, /turf/open/misc/beach/sand, /area/awaymission/museum/mothroachvoid) +"xp" = ( +/obj/structure/table, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "xr" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/structure/closet/crate/freezer/food, @@ -2668,6 +2951,11 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"xO" = ( +/obj/structure/table, +/obj/item/reagent_containers/cup/soda_cans/canned_laughter, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "xP" = ( /obj/structure/closet/crate/cardboard, /obj/item/storage/toolbox/artistic, @@ -2704,10 +2992,10 @@ /turf/open/chasm/true/no_smooth, /area/awaymission/museum) "yi" = ( +/mob/living/basic/mothroach, /obj/structure/chair/comfy/beige{ dir = 1 }, -/mob/living/basic/mothroach, /obj/effect/mapping_helpers/mob_buckler, /turf/open/floor/wood/large, /area/awaymission/museum) @@ -2731,9 +3019,9 @@ /turf/open/floor/holofloor/hyperspace/ns, /area/awaymission/museum) "yr" = ( +/mob/living/basic/mothroach, /obj/structure/chair/comfy, /obj/effect/mapping_helpers/mob_buckler, -/mob/living/basic/mothroach, /turf/open/floor/wood/large, /area/awaymission/museum) "yu" = ( @@ -2855,14 +3143,14 @@ /turf/closed/indestructible/rock, /area/awaymission/museum) "zG" = ( -/obj/structure/chair/office{ - dir = 8 - }, -/obj/effect/turf_decal/siding/red, /mob/living/basic/statue/mannequin{ dir = 8; hat = /obj/item/clothing/head/fedora }, +/obj/structure/chair/office{ + dir = 8 + }, +/obj/effect/turf_decal/siding/red, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/smooth_half, /area/awaymission/museum) @@ -2891,13 +3179,13 @@ pixel_y = 12 }, /obj/item/stamp/granted{ - pixel_y = 12; - pixel_x = 8 + pixel_x = 8; + pixel_y = 12 }, /obj/effect/spawner/random/bureaucracy/paper{ - spawn_random_offset = 12; + spawn_loot_count = 8; spawn_scatter_radius = 1; - spawn_loot_count = 8 + spawn_random_offset = 12 }, /turf/open/floor/iron, /area/awaymission/museum) @@ -3043,8 +3331,8 @@ teleport_y_offset = -30 }, /turf/open/mirage{ - target_turf_y = -29; - dir = 1 + dir = 1; + target_turf_y = -29 }, /area/awaymission/museum) "AP" = ( @@ -3054,6 +3342,13 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/indestructible/plating, /area/awaymission/museum) +"AQ" = ( +/obj/machinery/door/airlock{ + name = "Restroom Cabin 4"; + id_tag = "museum_toilet4" + }, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "AR" = ( /obj/structure/railing{ dir = 8 @@ -3217,9 +3512,9 @@ "Cy" = ( /obj/structure/table/wood, /obj/effect/spawner/random/food_or_drink/booze{ - loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer = 10, /obj/item/reagent_containers/cup/glass/bottle/ale = 10, /obj/item/reagent_containers/cup/glass/bottle/beer/light = 5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor = 5, /obj/item/reagent_containers/cup/glass/bottle/whiskey = 5, /obj/item/reagent_containers/cup/glass/bottle/gin = 5, /obj/item/reagent_containers/cup/glass/bottle/vodka = 5, /obj/item/reagent_containers/cup/glass/bottle/tequila = 5, /obj/item/reagent_containers/cup/glass/bottle/rum = 5, /obj/item/reagent_containers/cup/glass/bottle/vermouth = 5, /obj/item/reagent_containers/cup/glass/bottle/cognac = 5, /obj/item/reagent_containers/cup/glass/bottle/wine = 5, /obj/item/reagent_containers/cup/glass/bottle/kahlua = 5, /obj/item/reagent_containers/cup/glass/bottle/amaretto = 5, /obj/item/reagent_containers/cup/glass/bottle/hcider = 5, /obj/item/reagent_containers/cup/glass/bottle/absinthe = 5, /obj/item/reagent_containers/cup/glass/bottle/sake = 5, /obj/item/reagent_containers/cup/glass/bottle/grappa = 5, /obj/item/reagent_containers/cup/glass/bottle/applejack = 5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic = 5, /obj/item/reagent_containers/cup/bottle/ethanol = 2, /obj/item/reagent_containers/cup/glass/bottle/fernet = 2, /obj/item/reagent_containers/cup/glass/bottle/champagne = 2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium = 2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager = 2, /obj/item/reagent_containers/cup/glass/bottle/patron = 1, /obj/item/reagent_containers/cup/glass/bottle/kong = 1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine = 1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka = 1, /obj/item/reagent_containers/cup/glass/bottle/trappist = 1); - spawn_random_offset = 2; - spawn_loot_count = 2 + loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer=10, /obj/item/reagent_containers/cup/glass/bottle/ale=10, /obj/item/reagent_containers/cup/glass/bottle/beer/light=5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor=5, /obj/item/reagent_containers/cup/glass/bottle/whiskey=5, /obj/item/reagent_containers/cup/glass/bottle/gin=5, /obj/item/reagent_containers/cup/glass/bottle/vodka=5, /obj/item/reagent_containers/cup/glass/bottle/tequila=5, /obj/item/reagent_containers/cup/glass/bottle/rum=5, /obj/item/reagent_containers/cup/glass/bottle/vermouth=5, /obj/item/reagent_containers/cup/glass/bottle/cognac=5, /obj/item/reagent_containers/cup/glass/bottle/wine=5, /obj/item/reagent_containers/cup/glass/bottle/kahlua=5, /obj/item/reagent_containers/cup/glass/bottle/amaretto=5, /obj/item/reagent_containers/cup/glass/bottle/hcider=5, /obj/item/reagent_containers/cup/glass/bottle/absinthe=5, /obj/item/reagent_containers/cup/glass/bottle/sake=5, /obj/item/reagent_containers/cup/glass/bottle/grappa=5, /obj/item/reagent_containers/cup/glass/bottle/applejack=5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic=5, /obj/item/reagent_containers/cup/bottle/ethanol=2, /obj/item/reagent_containers/cup/glass/bottle/fernet=2, /obj/item/reagent_containers/cup/glass/bottle/champagne=2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium=2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager=2, /obj/item/reagent_containers/cup/glass/bottle/patron=1, /obj/item/reagent_containers/cup/glass/bottle/kong=1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine=1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka=1, /obj/item/reagent_containers/cup/glass/bottle/trappist=1); + spawn_loot_count = 2; + spawn_random_offset = 2 }, /obj/machinery/light/warm/directional/west, /turf/open/floor/wood/large, @@ -3255,6 +3550,11 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"CG" = ( +/obj/structure/mirror/directional/east, +/obj/structure/sink/directional/west, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "CI" = ( /obj/effect/turf_decal/caution, /obj/effect/decal/cleanable/dirt/dust, @@ -3293,6 +3593,18 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"Db" = ( +/obj/item/maneki_neko{ + pixel_y = 4 + }, +/obj/structure/table, +/obj/machinery/status_display/random_message{ + pixel_y = 32; + firstline_to_secondline = list(ACCEPTING="DONATIONS") + }, +/obj/structure/fluff/fake_vent, +/turf/open/floor/iron, +/area/awaymission/museum/cafeteria) "De" = ( /obj/effect/oneway{ dir = 8 @@ -3383,6 +3695,11 @@ /obj/machinery/light/floor, /turf/open/floor/iron/dark, /area/awaymission/museum) +"DX" = ( +/obj/structure/reagent_dispensers/water_cooler, +/obj/machinery/light/warm/directional/west, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "DY" = ( /turf/closed/indestructible/reinforced/titanium, /area/awaymission/museum) @@ -3400,8 +3717,8 @@ /obj/machinery/computer/old{ name = "replica computer"; dir = 4; - icon_keyboard = "rd_key"; - icon_screen = "rdcomp" + icon_screen = "rdcomp"; + icon_keyboard = "rd_key" }, /turf/open/floor/iron/smooth_large, /area/awaymission/museum) @@ -3421,8 +3738,8 @@ dir = 4 }, /obj/structure/fluff/wallsign/directional/south{ - dir = 4; - name = "Oddities" + name = "Oddities"; + dir = 4 }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -3435,12 +3752,17 @@ /turf/open/indestructible/plating, /area/awaymission/museum) "Er" = ( -/obj/structure/chair/comfy/beige, /mob/living/basic/mothroach, +/obj/structure/chair/comfy/beige, /obj/effect/mapping_helpers/mob_buckler, /obj/machinery/light/warm/directional/west, /turf/open/floor/wood/large, /area/awaymission/museum) +"Es" = ( +/obj/structure/table, +/obj/effect/spawner/random/food_or_drink/donkpockets, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "EA" = ( /obj/machinery/door/airlock/shuttle/glass, /obj/effect/turf_decal/siding/dark_blue, @@ -3460,9 +3782,9 @@ /area/awaymission/museum) "EG" = ( /obj/item/flashlight/flare{ - start_on = 1; icon_state = "flare-on"; - light_range = 4 + light_range = 4; + start_on = 1 }, /obj/effect/decal/cleanable/dirt/dust, /turf/open/indestructible/plating, @@ -3476,9 +3798,9 @@ "EM" = ( /obj/structure/table/wood, /obj/effect/spawner/random/food_or_drink/booze{ - loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer = 10, /obj/item/reagent_containers/cup/glass/bottle/ale = 10, /obj/item/reagent_containers/cup/glass/bottle/beer/light = 5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor = 5, /obj/item/reagent_containers/cup/glass/bottle/whiskey = 5, /obj/item/reagent_containers/cup/glass/bottle/gin = 5, /obj/item/reagent_containers/cup/glass/bottle/vodka = 5, /obj/item/reagent_containers/cup/glass/bottle/tequila = 5, /obj/item/reagent_containers/cup/glass/bottle/rum = 5, /obj/item/reagent_containers/cup/glass/bottle/vermouth = 5, /obj/item/reagent_containers/cup/glass/bottle/cognac = 5, /obj/item/reagent_containers/cup/glass/bottle/wine = 5, /obj/item/reagent_containers/cup/glass/bottle/kahlua = 5, /obj/item/reagent_containers/cup/glass/bottle/amaretto = 5, /obj/item/reagent_containers/cup/glass/bottle/hcider = 5, /obj/item/reagent_containers/cup/glass/bottle/absinthe = 5, /obj/item/reagent_containers/cup/glass/bottle/sake = 5, /obj/item/reagent_containers/cup/glass/bottle/grappa = 5, /obj/item/reagent_containers/cup/glass/bottle/applejack = 5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic = 5, /obj/item/reagent_containers/cup/bottle/ethanol = 2, /obj/item/reagent_containers/cup/glass/bottle/fernet = 2, /obj/item/reagent_containers/cup/glass/bottle/champagne = 2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium = 2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager = 2, /obj/item/reagent_containers/cup/glass/bottle/patron = 1, /obj/item/reagent_containers/cup/glass/bottle/kong = 1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine = 1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka = 1, /obj/item/reagent_containers/cup/glass/bottle/trappist = 1); - spawn_random_offset = 2; - spawn_loot_count = 2 + loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer=10, /obj/item/reagent_containers/cup/glass/bottle/ale=10, /obj/item/reagent_containers/cup/glass/bottle/beer/light=5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor=5, /obj/item/reagent_containers/cup/glass/bottle/whiskey=5, /obj/item/reagent_containers/cup/glass/bottle/gin=5, /obj/item/reagent_containers/cup/glass/bottle/vodka=5, /obj/item/reagent_containers/cup/glass/bottle/tequila=5, /obj/item/reagent_containers/cup/glass/bottle/rum=5, /obj/item/reagent_containers/cup/glass/bottle/vermouth=5, /obj/item/reagent_containers/cup/glass/bottle/cognac=5, /obj/item/reagent_containers/cup/glass/bottle/wine=5, /obj/item/reagent_containers/cup/glass/bottle/kahlua=5, /obj/item/reagent_containers/cup/glass/bottle/amaretto=5, /obj/item/reagent_containers/cup/glass/bottle/hcider=5, /obj/item/reagent_containers/cup/glass/bottle/absinthe=5, /obj/item/reagent_containers/cup/glass/bottle/sake=5, /obj/item/reagent_containers/cup/glass/bottle/grappa=5, /obj/item/reagent_containers/cup/glass/bottle/applejack=5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic=5, /obj/item/reagent_containers/cup/bottle/ethanol=2, /obj/item/reagent_containers/cup/glass/bottle/fernet=2, /obj/item/reagent_containers/cup/glass/bottle/champagne=2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium=2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager=2, /obj/item/reagent_containers/cup/glass/bottle/patron=1, /obj/item/reagent_containers/cup/glass/bottle/kong=1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine=1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka=1, /obj/item/reagent_containers/cup/glass/bottle/trappist=1); + spawn_loot_count = 2; + spawn_random_offset = 2 }, /turf/open/floor/wood/large, /area/awaymission/museum) @@ -3502,15 +3824,15 @@ /turf/open/floor/circuit/green, /area/awaymission/museum) "ET" = ( +/mob/living/basic/statue/mannequin{ + dir = 1 + }, /obj/structure/chair/stool/directional/north, /obj/effect/turf_decal/tile/green/opposingcorners, /obj/effect/decal/cleanable/dirt/dust, /obj/effect/turf_decal/siding/green{ dir = 1 }, -/mob/living/basic/statue/mannequin{ - dir = 1 - }, /turf/open/floor/iron, /area/awaymission/museum) "EU" = ( @@ -3520,8 +3842,8 @@ dir = 4 }, /obj/structure/fluff/wallsign/directional/north{ - dir = 4; - name = "Oddities" + name = "Oddities"; + dir = 4 }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -3594,8 +3916,8 @@ /area/awaymission/museum) "Fw" = ( /obj/effect/decal/cleanable/blood/tracks{ - should_dry = 0; - name = "replica blood" + name = "replica blood"; + should_dry = 0 }, /obj/effect/mapping_helpers/burnt_floor, /obj/effect/decal/cleanable/dirt/dust, @@ -3604,8 +3926,8 @@ "FA" = ( /obj/structure/table/reinforced, /obj/item/circuitboard{ - icon_state = "printer"; - name = "fancy replica tech" + name = "fancy replica tech"; + icon_state = "printer" }, /turf/open/floor/iron/smooth_large, /area/awaymission/museum) @@ -3642,6 +3964,32 @@ /obj/structure/sign/departments/restroom/directional/south, /turf/open/floor/wood/large, /area/awaymission/museum) +"FO" = ( +/obj/effect/decal/cleanable/crayon/puzzle/pin{ + puzzle_id = "museum_r_wing_puzzle" + }, +/turf/closed/indestructible/reinforced, +/area/awaymission/museum) +"FU" = ( +/obj/effect/turf_decal/siding/dark_blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/structure/fluff/wallsign/directional/south{ + name = "Cafeteria"; + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/awaymission/museum) +"FY" = ( +/obj/structure/chair/sofa/bench/left, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) +"Ga" = ( +/obj/effect/decal/cleanable/dirt/dust, +/obj/machinery/door/airlock/grunge, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "Gh" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/effect/turf_decal/siding/dark_blue{ @@ -3655,6 +4003,9 @@ /obj/structure/closet/crate/miningcar, /turf/open/floor/tram/plate, /area/awaymission/museum) +"Gn" = ( +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "Gv" = ( /obj/effect/decal/cleanable/crayon{ icon_state = "l"; @@ -3675,20 +4026,27 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron, /area/awaymission/museum) +"GB" = ( +/obj/structure/table, +/obj/item/reagent_containers/cup/soda_cans/thirteenloko{ + pixel_y = 7 + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "GE" = ( /obj/structure/plaque/static_plaque/golden/commission/meta, /turf/open/floor/mineral/gold, /area/awaymission/museum) "GG" = ( +/mob/living/basic/statue/mannequin{ + dir = 4 + }, /obj/structure/chair/office{ dir = 4 }, /obj/effect/turf_decal/siding/wood{ dir = 1 }, -/mob/living/basic/statue/mannequin{ - dir = 4 - }, /turf/open/floor/wood/tile, /area/awaymission/museum) "GO" = ( @@ -3762,8 +4120,8 @@ /area/awaymission/museum) "Hn" = ( /mob/living/basic/statue/mannequin{ - hat = /obj/item/clothing/head/costume/kitty; - dir = 8 + dir = 8; + hat = /obj/item/clothing/head/costume/kitty }, /obj/effect/turf_decal/trimline/yellow, /obj/effect/turf_decal/trimline/yellow/corner{ @@ -3797,7 +4155,7 @@ /area/awaymission/museum) "HA" = ( /obj/machinery/status_display/random_message{ - firstline_to_secondline = list("NO" = "LITTERING","YOU ARE" = "BEING WATCHED", "DO NOT TOUCH" = "THE EXHIBITS") + firstline_to_secondline = list(NO="LITTERING", "YOU ARE"="BEING WATCHED", "DO NOT TOUCH"="THE EXHIBITS") }, /turf/closed/indestructible/reinforced, /area/awaymission/museum) @@ -3806,7 +4164,7 @@ /obj/effect/turf_decal/siding/dark_blue, /obj/machinery/door/poddoor/shutters/indestructible{ dir = 8; - id = "nothing" + id = "museum_secret" }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -3838,6 +4196,9 @@ "HP" = ( /turf/open/floor/iron/freezer, /area/awaymission/museum) +"HQ" = ( +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "HU" = ( /obj/structure/railing{ dir = 4 @@ -3848,6 +4209,19 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/indestructible/plating, /area/awaymission/museum) +"HW" = ( +/obj/machinery/button/door/directional/north{ + name = "Lock Control"; + id = "museum_toilet1"; + specialfunctions = 4; + normaldoorcontrol = 1 + }, +/obj/structure/toilet/museum{ + dir = 4 + }, +/obj/machinery/light/small/dim/directional/west, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "HY" = ( /obj/machinery/light/floor, /obj/effect/decal/cleanable/dirt/dust, @@ -3857,6 +4231,12 @@ /obj/machinery/light/warm/dim/directional/north, /turf/open/floor/carpet, /area/awaymission/museum) +"Id" = ( +/obj/effect/decal/cleanable/crayon/puzzle/pin{ + puzzle_id = "museum_r_wing_puzzle" + }, +/turf/closed/indestructible/wood, +/area/awaymission/museum) "Ij" = ( /obj/machinery/door/airlock/external, /obj/effect/mapping_helpers/airlock/locked, @@ -3871,6 +4251,9 @@ /obj/structure/holosign/barrier/engineering, /turf/open/chasm/true/no_smooth, /area/awaymission/museum) +"Ir" = ( +/turf/closed/indestructible/fakedoor, +/area/awaymission/museum) "Iu" = ( /turf/open/floor/mineral/titanium/blue, /area/awaymission/museum) @@ -3898,6 +4281,12 @@ /obj/item/food/grilled_beef_gyro, /turf/open/floor/wood/tile, /area/awaymission/museum) +"IG" = ( +/obj/structure/fluff/fake_camera{ + dir = 9 + }, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "IH" = ( /obj/machinery/portable_atmospherics/canister/water_vapor, /obj/effect/decal/cleanable/dirt/dust, @@ -3914,11 +4303,22 @@ /obj/effect/turf_decal/stripes, /turf/open/floor/iron/smooth_large, /area/awaymission/museum) +"IX" = ( +/mob/living/basic/statue/mannequin{ + name = "cafeteria patron"; + held_item = /obj/item/knife + }, +/obj/structure/chair/sofa/bench{ + dir = 1 + }, +/obj/effect/mapping_helpers/mob_buckler, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "IZ" = ( +/mob/living/basic/mothroach, /obj/structure/chair/comfy/beige{ dir = 4 }, -/mob/living/basic/mothroach, /obj/effect/mapping_helpers/mob_buckler, /turf/open/floor/wood/large, /area/awaymission/museum) @@ -3959,6 +4359,13 @@ /obj/machinery/light/small/dim/directional/west, /turf/open/floor/carpet/executive, /area/awaymission/museum) +"Jl" = ( +/obj/machinery/status_display/random_message{ + pixel_x = 32; + firstline_to_secondline = list(CAFETERIA="YUMMY", CAFETERIA="HOTDOGS", ENJOY="YOUR MEAL") + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "Jn" = ( /obj/structure/table/reinforced, /obj/effect/spawner/random/entertainment/toy, @@ -3967,6 +4374,13 @@ /obj/effect/turf_decal/siding/dark_blue, /turf/open/floor/mineral/titanium/blue, /area/awaymission/museum) +"Jp" = ( +/obj/machinery/door/airlock{ + name = "Restroom Cabin 1"; + id_tag = "museum_toilet1" + }, +/turf/open/floor/iron, +/area/awaymission/museum) "Js" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/effect/turf_decal/siding/dark_blue{ @@ -4077,11 +4491,24 @@ /turf/open/floor/catwalk_floor, /area/awaymission/museum) "Kx" = ( -/obj/effect/decal/cleanable/dirt/dust, /mob/living/basic/skeleton, +/obj/effect/decal/cleanable/dirt/dust, /obj/effect/spawner/random/maintenance/three, /turf/open/indestructible/plating, /area/awaymission/museum) +"Kz" = ( +/obj/structure/sign/warning/no_smoking/directional/west, +/obj/machinery/computer/terminal/museum{ + name = "extinguisher manual terminal"; + dir = 4; + content = list("Instructions; stand upright; remove safety clip; aim noozle at base of fire... the rest is up to you.") + }, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) +"KB" = ( +/obj/structure/sign/warning/no_smoking/circle/directional/east, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "KI" = ( /turf/open/floor/iron/dark/side{ dir = 10 @@ -4112,7 +4539,7 @@ /area/awaymission/museum) "Lp" = ( /obj/machinery/status_display/random_message{ - firstline_to_secondline = list("NO" = "LITTERING","YOU ARE" = "BEING WATCHED", "DO NOT TOUCH" = "THE EXHIBITS") + firstline_to_secondline = list(NO="LITTERING", "YOU ARE"="BEING WATCHED", "DO NOT TOUCH"="THE EXHIBITS") }, /turf/closed/indestructible/reinforced/titanium/nodiagonal, /area/awaymission/museum) @@ -4162,6 +4589,12 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"LK" = ( +/obj/structure/table, +/obj/item/piggy_bank/museum, +/obj/machinery/light/warm/directional/north, +/turf/open/floor/iron, +/area/awaymission/museum/cafeteria) "LN" = ( /obj/effect/decal/cleanable/glass/titanium, /turf/open/floor/iron/dark/textured_large, @@ -4197,6 +4630,11 @@ "Ma" = ( /turf/open/chasm/true/no_smooth, /area/awaymission/museum) +"Mb" = ( +/obj/effect/turf_decal/siding/dark_blue, +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "Mg" = ( /obj/structure/plaque/static_plaque/golden/commission/tram, /turf/closed/indestructible/reinforced, @@ -4215,9 +4653,9 @@ /area/awaymission/museum) "Mv" = ( /mob/living/basic/statue/mannequin{ + name = "Michael Trasen"; dir = 4; - held_item = /obj/item/wrench; - name = "Michael Trasen" + held_item = /obj/item/wrench }, /obj/effect/turf_decal/stripes{ dir = 4 @@ -4300,10 +4738,16 @@ /obj/structure/plaque/static_plaque/golden/commission/birdboat, /turf/closed/indestructible/reinforced, /area/awaymission/museum) +"Nn" = ( +/obj/machinery/door/airlock/public{ + name = "Restrooms" + }, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "Ns" = ( /obj/structure/table/wood, /obj/effect/spawner/random/food_or_drink/booze{ - loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer = 10, /obj/item/reagent_containers/cup/glass/bottle/ale = 10, /obj/item/reagent_containers/cup/glass/bottle/beer/light = 5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor = 5, /obj/item/reagent_containers/cup/glass/bottle/whiskey = 5, /obj/item/reagent_containers/cup/glass/bottle/gin = 5, /obj/item/reagent_containers/cup/glass/bottle/vodka = 5, /obj/item/reagent_containers/cup/glass/bottle/tequila = 5, /obj/item/reagent_containers/cup/glass/bottle/rum = 5, /obj/item/reagent_containers/cup/glass/bottle/vermouth = 5, /obj/item/reagent_containers/cup/glass/bottle/cognac = 5, /obj/item/reagent_containers/cup/glass/bottle/wine = 5, /obj/item/reagent_containers/cup/glass/bottle/kahlua = 5, /obj/item/reagent_containers/cup/glass/bottle/amaretto = 5, /obj/item/reagent_containers/cup/glass/bottle/hcider = 5, /obj/item/reagent_containers/cup/glass/bottle/absinthe = 5, /obj/item/reagent_containers/cup/glass/bottle/sake = 5, /obj/item/reagent_containers/cup/glass/bottle/grappa = 5, /obj/item/reagent_containers/cup/glass/bottle/applejack = 5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic = 5, /obj/item/reagent_containers/cup/bottle/ethanol = 2, /obj/item/reagent_containers/cup/glass/bottle/fernet = 2, /obj/item/reagent_containers/cup/glass/bottle/champagne = 2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium = 2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager = 2, /obj/item/reagent_containers/cup/glass/bottle/patron = 1, /obj/item/reagent_containers/cup/glass/bottle/kong = 1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine = 1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka = 1, /obj/item/reagent_containers/cup/glass/bottle/trappist = 1); + loot = list(/obj/item/reagent_containers/cup/glass/bottle/beer=10, /obj/item/reagent_containers/cup/glass/bottle/ale=10, /obj/item/reagent_containers/cup/glass/bottle/beer/light=5, /obj/item/reagent_containers/cup/glass/bottle/maltliquor=5, /obj/item/reagent_containers/cup/glass/bottle/whiskey=5, /obj/item/reagent_containers/cup/glass/bottle/gin=5, /obj/item/reagent_containers/cup/glass/bottle/vodka=5, /obj/item/reagent_containers/cup/glass/bottle/tequila=5, /obj/item/reagent_containers/cup/glass/bottle/rum=5, /obj/item/reagent_containers/cup/glass/bottle/vermouth=5, /obj/item/reagent_containers/cup/glass/bottle/cognac=5, /obj/item/reagent_containers/cup/glass/bottle/wine=5, /obj/item/reagent_containers/cup/glass/bottle/kahlua=5, /obj/item/reagent_containers/cup/glass/bottle/amaretto=5, /obj/item/reagent_containers/cup/glass/bottle/hcider=5, /obj/item/reagent_containers/cup/glass/bottle/absinthe=5, /obj/item/reagent_containers/cup/glass/bottle/sake=5, /obj/item/reagent_containers/cup/glass/bottle/grappa=5, /obj/item/reagent_containers/cup/glass/bottle/applejack=5, /obj/item/reagent_containers/cup/glass/bottle/wine_voltaic=5, /obj/item/reagent_containers/cup/bottle/ethanol=2, /obj/item/reagent_containers/cup/glass/bottle/fernet=2, /obj/item/reagent_containers/cup/glass/bottle/champagne=2, /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium=2, /obj/item/reagent_containers/cup/glass/bottle/goldschlager=2, /obj/item/reagent_containers/cup/glass/bottle/patron=1, /obj/item/reagent_containers/cup/glass/bottle/kong=1, /obj/item/reagent_containers/cup/glass/bottle/lizardwine=1, /obj/item/reagent_containers/cup/glass/bottle/vodka/badminka=1, /obj/item/reagent_containers/cup/glass/bottle/trappist=1); spawn_random_offset = 2 }, /turf/open/floor/wood/large, @@ -4324,8 +4768,8 @@ /obj/structure/railing, /obj/structure/table, /obj/item/clothing/mask/cigarette/cigar{ - lit = 1; - icon_state = "cigaron" + icon_state = "cigaron"; + lit = 1 }, /turf/open/chasm/true/no_smooth, /area/awaymission/museum) @@ -4431,11 +4875,11 @@ /turf/open/floor/iron, /area/awaymission/museum) "OS" = ( +/mob/living/basic/mothroach, /obj/structure/chair/stool/bar/directional/east{ can_buckle = 1 }, /obj/effect/mapping_helpers/mob_buckler, -/mob/living/basic/mothroach, /turf/open/floor/wood/large, /area/awaymission/museum) "OT" = ( @@ -4481,6 +4925,9 @@ /obj/item/food/kebab/pineapple_skewer, /turf/open/floor/iron/freezer, /area/awaymission/museum) +"Ph" = ( +/turf/closed/indestructible/reinforced, +/area/awaymission/museum/cafeteria) "Pi" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/effect/turf_decal/siding/dark_blue/corner{ @@ -4584,8 +5031,8 @@ "PZ" = ( /obj/effect/mapping_helpers/broken_floor, /obj/item/flashlight/flare{ - start_on = 1; - icon_state = "flare-on" + icon_state = "flare-on"; + start_on = 1 }, /turf/open/indestructible/plating, /area/awaymission/museum) @@ -4756,9 +5203,9 @@ }, /obj/structure/fluff{ name = "replica prototype autolathe"; + desc = "A non-functional replica of a prototype Autolathe."; icon = 'icons/obj/machines/lathes.dmi'; - icon_state = "autolathe"; - desc = "A non-functional replica of a prototype Autolathe." + icon_state = "autolathe" }, /turf/open/floor/iron/smooth_corner{ dir = 4 @@ -4803,6 +5250,18 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/white, /area/awaymission/museum) +"RQ" = ( +/obj/effect/turf_decal/tile/neutral/opposingcorners, +/obj/effect/turf_decal/siding/dark_blue{ + dir = 1 + }, +/obj/structure/fluff/fake_camera, +/obj/effect/decal/puzzle_dots{ + pixel_y = -32; + id = "museum_r_wing_puzzle" + }, +/turf/open/floor/iron/dark, +/area/awaymission/museum) "RR" = ( /obj/machinery/suit_storage_unit/open, /obj/effect/turf_decal/box, @@ -4829,8 +5288,8 @@ /turf/open/floor/bluespace, /area/awaymission/museum) "Se" = ( -/obj/structure/chair/comfy/beige, /mob/living/basic/mothroach, +/obj/structure/chair/comfy/beige, /obj/effect/mapping_helpers/mob_buckler, /turf/open/floor/wood/large, /area/awaymission/museum) @@ -4839,6 +5298,11 @@ /obj/structure/railing, /turf/open/floor/holofloor/asteroid, /area/awaymission/museum) +"Sm" = ( +/obj/effect/decal/cleanable/dirt/dust, +/obj/structure/fluff/fake_scrubber, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "So" = ( /obj/machinery/shower/directional/east, /turf/open/floor/iron/white/textured_large, @@ -4849,7 +5313,7 @@ /area/awaymission/museum) "Sv" = ( /obj/machinery/status_display/random_message{ - firstline_to_secondline = list("SOUVENIR" = "SHOP") + firstline_to_secondline = list(SOUVENIR="SHOP") }, /turf/closed/indestructible/reinforced, /area/awaymission/museum) @@ -4880,7 +5344,11 @@ }, /obj/machinery/door/poddoor/shutters/indestructible{ dir = 8; - id = "nothing" + id = "museum_secret" + }, +/obj/machinery/puzzle/password/pin/directional/south{ + id = "museum_r_wing_puzzle"; + late_initialize_pop = 1 }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -4889,6 +5357,14 @@ /obj/item/stack/spacecash/c1000, /turf/open/indestructible/plating, /area/awaymission/museum) +"Ta" = ( +/obj/machinery/light/directional/west, +/obj/effect/decal/cleanable/crayon/puzzle/pin{ + pixel_x = -32; + puzzle_id = "museum_r_wing_puzzle" + }, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "Tr" = ( /obj/structure/plaque/static_plaque/golden/commission/kilo, /obj/machinery/light/floor, @@ -4923,6 +5399,13 @@ }, /turf/open/indestructible/plating, /area/awaymission/museum) +"TC" = ( +/obj/machinery/door/airlock/multi_tile/public/glass{ + name = "Cafeteria"; + dir = 4 + }, +/turf/open/floor/iron, +/area/awaymission/museum/cafeteria) "TF" = ( /turf/open/floor/iron/stairs/right{ dir = 4 @@ -4998,8 +5481,8 @@ dir = 8 }, /obj/structure/fluff/wallsign/directional/east{ - dir = 2; - name = "Exit" + name = "Exit"; + dir = 2 }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -5037,8 +5520,8 @@ /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/structure/table, /obj/item/computer_disk{ - icon_state = "datadisk_hydro"; - name = "plant data disk" + name = "plant data disk"; + icon_state = "datadisk_hydro" }, /turf/open/floor/iron/dark, /area/awaymission/museum) @@ -5066,6 +5549,12 @@ /mob/living/basic/mothroach, /turf/open/misc/beach/sand, /area/awaymission/museum/mothroachvoid) +"UH" = ( +/obj/structure/mirror/broken/directional/east, +/obj/structure/sink/directional/west, +/obj/effect/decal/cleanable/oil/slippery, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "UI" = ( /obj/effect/turf_decal/tile/green/opposingcorners, /obj/effect/decal/cleanable/dirt/dust, @@ -5106,8 +5595,8 @@ /area/awaymission/museum) "UT" = ( /obj/machinery/status_display/random_message{ - firstline_to_secondline = list("NO" = "LITTERING","YOU ARE" = "BEING WATCHED", "DO NOT TOUCH" = "THE EXHIBITS"); - pixel_x = 32 + pixel_x = 32; + firstline_to_secondline = list(NO="LITTERING", "YOU ARE"="BEING WATCHED", "DO NOT TOUCH"="THE EXHIBITS") }, /turf/open/floor/carpet, /area/awaymission/museum) @@ -5129,6 +5618,14 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/iron/smooth_half, /area/awaymission/museum) +"Vb" = ( +/obj/effect/replica_spawner{ + obvious_replica = 0; + pixel_y = 30; + target_path = /obj/structure/sign/painting/eldritch/weeping + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "Vc" = ( /obj/machinery/light/warm/directional/east, /obj/effect/turf_decal/tile/neutral/opposingcorners, @@ -5380,6 +5877,25 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"WY" = ( +/obj/machinery/button/door/directional/north{ + name = "Lock Control"; + id = "museum_toilet2"; + specialfunctions = 4; + normaldoorcontrol = 1 + }, +/obj/structure/toilet/museum{ + dir = 4 + }, +/obj/machinery/light/small/dim/directional/west, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) +"WZ" = ( +/obj/structure/chair/sofa/bench{ + dir = 1 + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "Xa" = ( /obj/structure/broken_flooring/corner/always_floorplane{ dir = 2 @@ -5402,6 +5918,10 @@ "Xh" = ( /turf/closed/indestructible/fakedoor/maintenance, /area/awaymission/museum) +"Xi" = ( +/obj/structure/chair/sofa/bench/right, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "Xo" = ( /obj/structure/lattice/catwalk/mining, /obj/structure/railing, @@ -5502,8 +6022,8 @@ /turf/closed/indestructible/reinforced/titanium/nodiagonal, /area/awaymission/museum) "Yg" = ( -/obj/effect/decal/cleanable/dirt/dust, /mob/living/basic/cockroach, +/obj/effect/decal/cleanable/dirt/dust, /turf/open/indestructible/plating, /area/awaymission/museum) "Yk" = ( @@ -5512,6 +6032,12 @@ }, /turf/open/floor/iron, /area/awaymission/museum) +"Yn" = ( +/obj/machinery/light/warm/directional/east, +/obj/effect/decal/cleanable/dirt/dust, +/obj/structure/fluff/fake_camera, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "Yr" = ( /obj/effect/turf_decal/tile/neutral/opposingcorners, /obj/structure/table/reinforced, @@ -5521,13 +6047,21 @@ /obj/item/reagent_containers/cup/glass/mug/nanotrasen, /turf/open/floor/iron/dark, /area/awaymission/museum) +"Yy" = ( +/obj/machinery/computer/terminal/museum{ + name = "donation info terminal"; + dir = 8; + content = list("We're once again asking for your financial support; We love our job, but love alone can only get us so far. Please consider leaving a donation to help keep the musuem running. Thank you.") + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "Yz" = ( -/obj/effect/turf_decal/siding, /mob/living/basic/statue/mannequin{ dir = 8; held_item = /obj/item/pickaxe; hat = /obj/item/clothing/suit/hooded/explorer }, +/obj/effect/turf_decal/siding, /obj/structure/railing, /turf/open/floor/holofloor/asteroid, /area/awaymission/museum) @@ -5571,8 +6105,15 @@ /obj/item/reagent_containers/cup/glass/coffee/no_lid{ pixel_x = 12 }, +/obj/item/paper/fluff/museum/numbers_on_walls, /turf/open/floor/iron, /area/awaymission/museum) +"Za" = ( +/obj/structure/chair/sofa/bench/right{ + dir = 1 + }, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "Zf" = ( /obj/structure/plaque/static_plaque/golden/commission/donut, /turf/closed/indestructible/reinforced, @@ -5589,6 +6130,11 @@ /obj/structure/lattice, /turf/open/chasm/true/no_smooth, /area/awaymission/museum) +"Zo" = ( +/obj/structure/table, +/obj/effect/spawner/random/food_or_drink/snack, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "Zp" = ( /obj/structure/plaque/static_plaque/golden/commission/icebox, /turf/closed/indestructible/reinforced, @@ -5613,6 +6159,16 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"Zx" = ( +/obj/structure/chair/sofa/bench, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) +"Zz" = ( +/obj/effect/mapping_helpers/turn_off_lights_with_lightswitch, +/obj/machinery/light_switch/directional/west, +/turf/open/floor/iron/cafeteria, +/area/awaymission/museum/cafeteria) "ZB" = ( /obj/machinery/door/airlock/maintenance_hatch, /obj/structure/broken_flooring/side/always_floorplane/directional/east, @@ -5644,6 +6200,13 @@ }, /turf/open/floor/iron/dark, /area/awaymission/museum) +"ZO" = ( +/obj/structure/sink/directional/south{ + pixel_y = 6 + }, +/obj/structure/mirror/directional/north, +/turf/open/floor/iron/white/small, +/area/awaymission/museum) "ZP" = ( /obj/structure/statue/gold/ce{ anchored = 1 @@ -26150,7 +26713,7 @@ re re re re -re +Uq re re re @@ -26403,12 +26966,12 @@ re re re re -re -re -re -re -re -re +FK +FK +FK +FK +FK +Uq re re re @@ -26660,11 +27223,11 @@ re re re re -re -re -re -re -re +FK +kZ +FK +oD +FK re re re @@ -26915,13 +27478,13 @@ Wc FK re re -re -re -re -re -re -re -re +FK +FO +FK +ta +FK +AQ +FK re re re @@ -27172,13 +27735,13 @@ fy FK re re -re -re -re -re -re -re -re +FK +jO +ry +Gn +Ta +Gn +FK re re re @@ -27429,13 +27992,13 @@ tD FK re re -re -re -re -re -re -re -re +FK +FK +FO +IG +Gn +ax +FK re re re @@ -27687,12 +28250,12 @@ FK re re re -re -re -re -re -re -re +FK +ZO +Gn +Gn +Gn +FK re re re @@ -27947,7 +28510,7 @@ FK FK FK FK -FK +Nn FK FK Uq @@ -29699,7 +30262,7 @@ aW MF MF MF -MF +Id Re PZ Ma @@ -34375,7 +34938,7 @@ FK FK FK FK -FK +FO FK FK FK @@ -35113,7 +35676,7 @@ re re re re -FK +FO qw XK PP @@ -35916,7 +36479,7 @@ FK FK FK Yd -FK +FO NO dZ mn @@ -35958,6 +36521,9 @@ nu FK FK FK +FK +FK +FK Uq re re @@ -35995,9 +36561,6 @@ re re re re -re -re -re "} (119,1,1) = {" re @@ -36214,10 +36777,10 @@ qQ xI gT Qb -FK -re -re -re +Ga +gg +iJ +FO re re re @@ -36472,6 +37035,9 @@ xI xd ns FK +FK +FK +FK re re re @@ -36509,9 +37075,6 @@ re re re re -re -re -re "} (121,1,1) = {" re @@ -36731,7 +37294,7 @@ BO FK re re -re +Uq re re re @@ -36986,7 +37549,7 @@ Nc FK FK FK -Uq +re re re re @@ -37461,7 +38024,7 @@ FK FK FK PB -FK +FO Ma Ma Ma @@ -40781,7 +41344,7 @@ FK FK FK FK -lb +eW uY rQ FK @@ -42582,7 +43145,7 @@ FK FK lb uY -qX +RQ FK FK FK @@ -43085,19 +43648,19 @@ re re re re +FK +FK +FK +FK +FK +FK +FK re -re -re -re -re -re -re -re -Uq -re -re -re -Uq +FK +aa +uY +ci +jC re re re @@ -43342,19 +43905,19 @@ re re re re -re -re -re -re -re -re -re -re -re -re -re -re -re +FK +aO +FK +WY +FK +HW +FK +FK +FK +Mb +TM +wV +FK re re re @@ -43599,19 +44162,19 @@ re re re re -re -re -re -re -re -re -re -re -re -re -re -re -re +FK +wg +FK +us +FK +Jp +FK +um +FK +Mb +uY +wV +FK re re re @@ -43856,19 +44419,19 @@ re re re re -re -re -re -re -re -re -re -re -re -re -re -re -re +FK +Gn +Gn +Gn +Sm +Gn +Kz +Gn +FK +ju +uY +ci +Ir re re re @@ -44113,19 +44676,19 @@ re re re re -re -re -re -re -re -re -re -re -re -re -re -re -re +FK +nY +Gn +Gn +Gn +Gn +Gn +Gn +kx +uY +uY +wV +FK re re re @@ -44370,19 +44933,19 @@ re re re re -re -re -re -re -re -re -re -re -re -re -re -re -re +FK +ut +UH +gE +CG +Gn +CG +Yn +FK +RC +TM +FU +FK re re re @@ -44627,20 +45190,20 @@ re re re re -re -re -re -re -re -re -re -re -re -re -re -re -re -re +FK +FK +FK +FK +FK +FK +FK +FK +FK +Mb +uY +wV +FK +Uq re re re @@ -44892,11 +45455,11 @@ re re re re -re -re -re -re -re +FK +pN +uY +hS +oP re re re @@ -45149,11 +45712,11 @@ re re re re -re -re -re -re -re +FK +FK +pz +kL +FK re re re @@ -45403,16 +45966,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +Ph +Ph +Ph +Ph +ms +TC +Ph +Ph +Ph re re re @@ -45660,16 +46223,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +DX +HQ +fl +Zz +HQ +HQ +HQ +ac +Ph re re re @@ -45917,16 +46480,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +qK +HQ +HQ +HQ +HQ +HQ +HQ +HQ +vG re re re @@ -46174,16 +46737,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +uH +HQ +Xi +xp +nZ +Xi +Es +nZ +Ph re re re @@ -46431,17 +46994,17 @@ re re re re -re -re -re -re -re -re -re -re -re -re -re +Ph +Vb +HQ +Zx +hm +WZ +ql +xp +WZ +Ph +Uq re re re @@ -46688,16 +47251,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +HQ +HQ +ql +Zo +WZ +ql +GB +IX +Ph re re re @@ -46945,16 +47508,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +Db +HQ +FY +xO +Za +FY +xp +Za +Ph re re re @@ -47202,16 +47765,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +LK +Yy +HQ +HQ +KB +Jl +HQ +cY +Ph re re re @@ -47459,16 +48022,16 @@ re re re re -re -re -re -re -re -re -re -re -re -re +Ph +Ph +Ph +Ph +Ph +Ph +Ph +Ph +Ph +Ph re re re diff --git a/_maps/map_files/Deathmatch/meatower.dmm b/_maps/map_files/Deathmatch/meatower.dmm index 47613f8988554..fb2359fa28879 100644 --- a/_maps/map_files/Deathmatch/meatower.dmm +++ b/_maps/map_files/Deathmatch/meatower.dmm @@ -198,7 +198,7 @@ /obj/machinery/conveyor/auto{ dir = 4 }, -/obj/item/pizzabox/margherita/robo, +/obj/item/pizzabox/margherita, /obj/effect/turf_decal/siding/dark{ dir = 1 }, diff --git a/_maps/map_files/Deathmatch/mech_madness.dmm b/_maps/map_files/Deathmatch/mech_madness.dmm index d36ccfb16cc20..3f804d2ee5452 100644 --- a/_maps/map_files/Deathmatch/mech_madness.dmm +++ b/_maps/map_files/Deathmatch/mech_madness.dmm @@ -664,7 +664,7 @@ /area/deathmatch) "ot" = ( /obj/structure/mop_bucket/janitorialcart{ - dir = 4 + dir = 4 }, /turf/open/floor/engine, /area/deathmatch) diff --git a/_maps/map_files/Deathmatch/starwars.dmm b/_maps/map_files/Deathmatch/starwars.dmm index c1f005461073a..c7654810360e1 100644 --- a/_maps/map_files/Deathmatch/starwars.dmm +++ b/_maps/map_files/Deathmatch/starwars.dmm @@ -1142,9 +1142,6 @@ }, /turf/open/indestructible/large, /area/deathmatch) -"US" = ( -/turf/closed/indestructible/reinforced, -/area/deathmatch) "Ve" = ( /obj/effect/turf_decal/tile/red/full, /obj/structure/barricade/security/murderdome, @@ -1320,7 +1317,7 @@ tV tV IC IC -US +KX Vz sZ sZ diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 28143ce53ea8b..30e548835e8be 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -8724,10 +8724,10 @@ "cdJ" = ( /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{ dir = 1 }, -/obj/structure/cable, /turf/open/floor/iron/dark/corner{ dir = 1 }, @@ -10426,7 +10426,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/newscaster/directional/west, /obj/structure/cable, -/turf/open/floor/iron, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/turf/open/floor/iron/dark/corner{ + dir = 1 + }, /area/station/hallway/secondary/exit/departure_lounge) "czL" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -36979,7 +36984,6 @@ /turf/open/floor/iron, /area/station/security/prison/garden) "jgl" = ( -/obj/machinery/door/firedoor, /obj/machinery/door/airlock/grunge{ name = "Morgue" }, diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index fc007a7d80a17..759e4f09d46df 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -8403,11 +8403,10 @@ /turf/open/floor/plating, /area/station/science/xenobiology) "cyo" = ( -/obj/machinery/stasis{ - dir = 4 +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 9 }, -/obj/effect/turf_decal/tile/blue/full, -/turf/open/floor/iron/large, +/turf/open/floor/iron/white, /area/station/medical/treatment_center) "cyA" = ( /obj/machinery/door/airlock/maintenance, @@ -20593,10 +20592,12 @@ /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/trimline/blue/filled/corner, /obj/effect/turf_decal/trimline/blue/filled/corner{ dir = 8 }, +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 4 + }, /turf/open/floor/iron/white, /area/station/medical/medbay/central) "gnh" = ( @@ -33374,16 +33375,12 @@ /turf/open/floor/iron, /area/station/maintenance/disposal/incinerator) "kmG" = ( -/obj/structure/table/glass, -/obj/item/reagent_containers/cup/bottle/epinephrine, -/obj/item/reagent_containers/cup/bottle/multiver{ - pixel_x = 6 - }, -/obj/item/reagent_containers/syringe, /obj/machinery/defibrillator_mount/directional/north, -/obj/item/radio/intercom/directional/west, /obj/effect/turf_decal/tile/blue/full, /obj/machinery/light/cold/directional/north, +/obj/machinery/stasis{ + dir = 4 + }, /turf/open/floor/iron/large, /area/station/medical/treatment_center) "kmH" = ( @@ -33549,15 +33546,6 @@ }, /turf/open/floor/iron, /area/mine/laborcamp) -"kpO" = ( -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/blue/filled/corner{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/medical/treatment_center) "kpX" = ( /obj/structure/bed/medical{ dir = 4 @@ -45378,7 +45366,8 @@ /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/trimline/blue/filled/corner, +/obj/effect/turf_decal/trimline/blue/filled/line, +/obj/machinery/airalarm/directional/south, /turf/open/floor/iron/white, /area/station/medical/medbay/central) "nRm" = ( @@ -51938,13 +51927,6 @@ "pOL" = ( /turf/open/floor/iron/white, /area/station/science/ordnance) -"pOU" = ( -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 6 - }, -/obj/machinery/airalarm/directional/south, -/turf/open/floor/iron/white, -/area/station/medical/medbay/central) "pOV" = ( /obj/structure/rack, /obj/item/clothing/suit/hazardvest, @@ -52893,6 +52875,17 @@ "qfh" = ( /turf/open/floor/iron/recharge_floor, /area/station/science/robotics/mechbay) +"qfi" = ( +/obj/structure/table/glass, +/obj/item/reagent_containers/cup/bottle/epinephrine, +/obj/item/reagent_containers/cup/bottle/multiver{ + pixel_x = 6 + }, +/obj/item/reagent_containers/syringe, +/obj/effect/turf_decal/tile/blue/full, +/obj/item/radio/intercom/directional/west, +/turf/open/floor/iron/large, +/area/station/medical/treatment_center) "qfj" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 @@ -66445,6 +66438,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/trimline/blue/filled/corner, /turf/open/floor/iron/white, /area/station/medical/medbay/central) "upa" = ( @@ -246023,7 +246017,7 @@ cXu frP oIJ nRd -pOU +lwQ tHr tHr bxU @@ -246281,7 +246275,7 @@ eqp cTV fHz lwQ -lwQ +qfi sEK eHg ahL @@ -246540,7 +246534,7 @@ sFG lwQ kmG cyo -kpO +lup lup aCA jDn @@ -246796,8 +246790,8 @@ mcW fPb bEL efK -evp juw +nji tkf ikz oSQ diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 443f7049dea71..7ac2807b4801e 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -43180,8 +43180,8 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/airlock/research/glass{ - name = "Chemistry Lab" +/obj/machinery/door/airlock/medical/glass{ + name = "Chemistry" }, /obj/effect/mapping_helpers/airlock/access/all/medical/chemistry, /obj/machinery/door/firedoor, @@ -45173,7 +45173,6 @@ }, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/mapping_helpers/airlock/access/any/medical/general, /obj/effect/mapping_helpers/airlock/access/any/medical/pharmacy, /turf/open/floor/plating, /area/station/maintenance/department/medical/central) @@ -50097,7 +50096,7 @@ name = "Surgery C Maintenance" }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/mapping_helpers/airlock/access/all/medical/general, +/obj/effect/mapping_helpers/airlock/access/all/medical/surgery, /turf/open/floor/plating, /area/station/maintenance/aft/greater) "rTL" = ( @@ -62655,13 +62654,12 @@ "whx" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/airlock/research/glass{ +/obj/machinery/door/airlock/medical/glass{ name = "Pharmacy" }, /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/mapping_helpers/airlock/access/any/medical/general, /obj/effect/mapping_helpers/airlock/access/any/medical/pharmacy, /obj/effect/turf_decal/tile/yellow/fourcorners, /obj/effect/landmark/navigate_destination, diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index ba8942326bc94..ae87f6f63578c 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -10478,6 +10478,7 @@ c_tag = "Service - Hydroponics" }, /obj/effect/turf_decal/tile/green/fourcorners, +/obj/item/storage/box/syringes, /turf/open/floor/iron/dark, /area/station/service/hydroponics) "cAd" = ( @@ -33756,6 +33757,7 @@ }, /obj/effect/turf_decal/tile/green/fourcorners, /obj/machinery/newscaster/directional/south, +/obj/item/reagent_containers/cup/watering_can, /turf/open/floor/iron/dark, /area/station/service/hydroponics) "kUm" = ( @@ -62927,6 +62929,7 @@ preset_destination_names = list("2" = "Hydroponics", "3" = "Kitchen") }, /obj/effect/turf_decal/tile/green/fourcorners, +/obj/item/reagent_containers/cup/watering_can, /turf/open/floor/iron/dark, /area/station/service/hydroponics) "vcv" = ( diff --git a/_maps/virtual_domains/stairs_and_cliffs.dmm b/_maps/virtual_domains/stairs_and_cliffs.dmm index 3c9e0711e0282..accdf93bf00be 100644 --- a/_maps/virtual_domains/stairs_and_cliffs.dmm +++ b/_maps/virtual_domains/stairs_and_cliffs.dmm @@ -5,6 +5,10 @@ }, /turf/open/cliff/snowrock/virtual_domain, /area/icemoon/underground/explored/virtual_domain) +"bU" = ( +/mob/living/basic/bear/snow/ancient, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) "cu" = ( /obj/effect/turf_decal/weather/snow/corner{ dir = 4 @@ -19,6 +23,10 @@ /obj/structure/chair/sofa/bench, /turf/open/floor/plating/snowed/smoothed, /area/icemoon/underground/explored/virtual_domain) +"cS" = ( +/obj/structure/barricade/wooden/snowed, +/turf/open/floor/iron/stairs, +/area/icemoon/underground/explored/virtual_domain) "dR" = ( /turf/open/misc/asteroid/snow, /area/icemoon/underground/explored/virtual_domain) @@ -34,6 +42,14 @@ /obj/structure/railing/corner, /turf/open/cliff/snowrock/virtual_domain, /area/icemoon/underground/explored/virtual_domain) +"gF" = ( +/obj/effect/landmark/bitrunning/curiosity_spawn, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) +"gY" = ( +/obj/effect/decal/remains/human, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) "hc" = ( /obj/structure/railing/corner/end{ dir = 8 @@ -65,6 +81,10 @@ /obj/structure/flora/tree/pine/style_random, /turf/open/misc/asteroid/snow, /area/icemoon/underground/explored/virtual_domain) +"mr" = ( +/obj/structure/fluff/fokoff_sign, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) "mx" = ( /obj/structure/railing, /obj/structure/railing{ @@ -86,6 +106,13 @@ }, /turf/open/floor/wood, /area/icemoon/underground/explored/virtual_domain) +"nY" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/railing, +/turf/open/floor/wood, +/area/icemoon/underground/explored/virtual_domain) "pl" = ( /obj/structure/bonfire/prelit, /turf/open/misc/asteroid/snow, @@ -597,10 +624,10 @@ dR dR dR dR -sM -sM -sM -sM +Nv +Nv +Nv +dR sM sM sM @@ -672,23 +699,23 @@ Qv Qv kK sw +gY dR -dR -sM -sM -sM -sM -sM -sM -sM -sM -sM sM sM sM +dR +cS +Nv +Nv +dR +dR +sw sM sM sM +qc +gF sM sM sM @@ -754,18 +781,18 @@ dR sM sM sM +sa sM sM +gY +dR +dR +sa +dR sM sM -sM -sM -sM -sM -sM -sM -sM -sM +Dz +dR sM sM sM @@ -827,21 +854,21 @@ Qv eB sw dR -dR -sM -sM -sM -sM -sM -sM +mr sM sM sM sM sM sM +mr +sa +dR +dR +gY sM sM +nY sM sM sM @@ -901,24 +928,24 @@ Am Am Qv Qv -dR +mr eB dR -sw +dR dR sM sM sM sM sM -sM -sM -sM -sM -sM -sM -sM -sM +kK +dR +bU +dR +dR +cS +Nv +hc sM sM sM @@ -989,10 +1016,10 @@ sM sM sM sM -sM -sM -sM -sM +dR +sa +gY +dR sM sM sM @@ -1063,13 +1090,13 @@ dR sM sM sM +pL sM sM -sM -sM -sM -sM -sM +sw +dR +dR +sa sM sM sM @@ -1140,13 +1167,13 @@ sM sM sM sM +pL +pL sM sM -sM -sM -sM -sM -sM +dR +dR +sw sM sM sM @@ -1217,8 +1244,8 @@ sM sM sM sM -sM -sM +pL +pL sM sM sM @@ -1295,11 +1322,11 @@ sM sM sM sM -sM -sM -sM -sM -sM +pL +pL +pL +pL +pL sM sM sM @@ -1373,8 +1400,8 @@ sM sM sM sM -sM -sM +pL +pL sM sM sM diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm index 55b5b12b531ac..75dccb49fdae3 100644 --- a/code/__DEFINES/_flags.dm +++ b/code/__DEFINES/_flags.dm @@ -193,6 +193,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define INDESTRUCTIBLE (1<<6) /// can't be frozen #define FREEZE_PROOF (1<<7) +/// can't be shuttle crushed. +#define SHUTTLE_CRUSH_PROOF (1<<8) //tesla_zap #define ZAP_MACHINE_EXPLOSIVE (1<<0) diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index 90726effb00fe..f00264d6b1a9f 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -193,6 +193,11 @@ /// HUNTERS #define ACCESS_HUNTER "hunter" +/// - - - MISC - - - + // These don't really fit anywhere else +/// For things that aren't ever supposed to be accessed +#define ACCESS_INACCESSIBLE "inaccessible" + /// - - - END ACCESS IDS - - - /// A list of access levels that, when added to an ID card, will warn admins. @@ -294,6 +299,7 @@ ACCESS_DETECTIVE, \ ACCESS_ENGINE_EQUIP, \ ACCESS_ENGINEERING, \ + ACCESS_EVA, \ ACCESS_EXTERNAL_AIRLOCKS, \ ACCESS_GATEWAY, \ ACCESS_GENETICS, \ diff --git a/code/__DEFINES/alerts.dm b/code/__DEFINES/alerts.dm index 1933b592d55c5..3d09a3e5a22d5 100644 --- a/code/__DEFINES/alerts.dm +++ b/code/__DEFINES/alerts.dm @@ -51,7 +51,6 @@ #define ALERT_MECH_DAMAGE "mech_damage" /** Food related */ -#define ALERT_NUTRITION "nutrition" #define ALERT_DISGUST "disgust" /** Environment related */ diff --git a/code/__DEFINES/atmospherics/atmos_piping.dm b/code/__DEFINES/atmospherics/atmos_piping.dm index 1993f10222523..3870a7aed34ac 100644 --- a/code/__DEFINES/atmospherics/atmos_piping.dm +++ b/code/__DEFINES/atmospherics/atmos_piping.dm @@ -9,11 +9,13 @@ #define EAST_SHORTPIPE (1<<6) #define WEST_SHORTPIPE (1<<7) // Helpers to convert cardinals to and from pipe bitfields -// Assumes X_FULLPIPE = X, X_SHORTPIPE >> 4 = X as above +// Assumes X_FULLPIPE = X, X_SHORTPIPE >> 4 = X, X_PIPECAPS >> 8 = X as above #define FULLPIPE_TO_CARDINALS(bitfield) ((bitfield) & ALL_CARDINALS) #define SHORTPIPE_TO_CARDINALS(bitfield) (((bitfield) >> 4) & ALL_CARDINALS) +#define PIPECAPS_TO_CARDINALS(bitfield) (((bitfield) >> 8) & ALL_CARDINALS) #define CARDINAL_TO_FULLPIPES(cardinals) (cardinals) #define CARDINAL_TO_SHORTPIPES(cardinals) ((cardinals) << 4) +#define CARDINAL_TO_PIPECAPS(cardinals) ((cardinals) << 8) // A pipe is a stub if it only has zero or one permitted direction. For a regular pipe this is nonsensical, and there are no pipe sprites for this, so it is not allowed. #define ISSTUB(bits) !((bits) & ((bits) - 1)) #define ISNOTSTUB(bits) ((bits) & ((bits) - 1)) diff --git a/code/__DEFINES/client.dm b/code/__DEFINES/client.dm index 0914bc025adda..17571b5270bb1 100644 --- a/code/__DEFINES/client.dm +++ b/code/__DEFINES/client.dm @@ -1,11 +1,6 @@ /// Checks if the given target is either a client or a mock client #define IS_CLIENT_OR_MOCK(target) (istype(target, /client) || istype(target, /datum/client_interface)) -/// Ensures that the client has been fully initialized via New(), and can't somehow execute actions before that. Security measure. -/// WILL RETURN OUT OF THE ENTIRE PROC COMPLETELY IF THE CLIENT IS NOT FULLY INITIALIZED. BE WARNED IF YOU WANT RETURN VALUES. -#define VALIDATE_CLIENT(target)\ - if (!target.fully_created) {\ - to_chat(target, span_warning("You are not fully initialized yet! Please wait a moment."));\ - log_access("Client [key_name(target)] attempted to execute a verb before being fully initialized.");\ - return\ - } +/// Checks to see if a /client has fully gone through New() as a safeguard against certain operations. +/// Should return the boolean value of the fully_created var, which should be TRUE if New() has finished running. FALSE otherwise. +#define VALIDATE_CLIENT_INITIALIZATION(target) (target.fully_created) diff --git a/code/__DEFINES/construction/material.dm b/code/__DEFINES/construction/material.dm index 24ab2eb330327..445b4e0dc88e4 100644 --- a/code/__DEFINES/construction/material.dm +++ b/code/__DEFINES/construction/material.dm @@ -15,8 +15,8 @@ #define MAXCOIL 30 //Category of materials -/// Is the material from an ore? currently unused but exists atm for categorizations sake -#define MAT_CATEGORY_ORE "ore capable" +/// Can this material be stored in the ore silo +#define MAT_CATEGORY_SILO "silo capable" /// Hard materials, such as iron or silver #define MAT_CATEGORY_RIGID "rigid material" /// Materials that can be used to craft items @@ -68,13 +68,13 @@ //Stock market stock values. /// How much quantity of a material stock exists for common materials like iron & glass. -#define MATERIAL_QUANTITY_COMMON 25000 +#define MATERIAL_QUANTITY_COMMON 5000 /// How much quantity of a material stock exists for uncommon materials like silver & titanium. -#define MATERIAL_QUANTITY_UNCOMMON 10000 +#define MATERIAL_QUANTITY_UNCOMMON 1000 /// How much quantity of a material stock exists for rare materials like gold, uranium, & diamond. -#define MATERIAL_QUANTITY_RARE 2500 +#define MATERIAL_QUANTITY_RARE 200 /// How much quantity of a material stock exists for exotic materials like diamond & bluespace crystals. -#define MATERIAL_QUANTITY_EXOTIC 500 +#define MATERIAL_QUANTITY_EXOTIC 50 // The number of ore vents that will spawn boulders with this material. /// Is this material going to spawn often in ore vents? (80% of vents on lavaland) 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 3ddd0eb85387b..b1914cc966bd4 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -155,9 +155,9 @@ #define ZIMPACT_NO_SPIN (1<<2) /// From mob/living/try_speak(): (message, ignore_spam, forced) -#define COMSIG_LIVING_TRY_SPEECH "living_vocal_speech" - /// Return if the mob can speak the message, regardless of any other signal returns or checks. - #define COMPONENT_CAN_ALWAYS_SPEAK (1<<0) +#define COMSIG_MOB_TRY_SPEECH "living_vocal_speech" + /// Return to skip can_speak check, IE, forcing success. Overrides below. + #define COMPONENT_IGNORE_CAN_SPEAK (1<<0) /// Return if the mob cannot speak. #define COMPONENT_CANNOT_SPEAK (1<<1) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 3654b4cfce5d3..91dbba15ff4d6 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -158,6 +158,9 @@ /// Return to prevent the default behavior (attack_selfing) from ocurring. #define COMPONENT_ITEM_ACTION_SLOT_INVALID (1<<0) +/// Sent from /obj/item/attack_atom(): (atom/attacked_atom, mob/living/user) +#define COMSIG_ITEM_POST_ATTACK_ATOM "item_post_attack_atom" + ///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) #define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" ///from base of obj/item/hit_reaction(): (owner, hitby, attack_text, final_block_chance, damage, attack_type, damage_type) @@ -308,9 +311,11 @@ // /obj/item/radio signals +///called from base of /obj/item/proc/talk_into(): (atom/movable/speaker, message, channel, list/spans, language, list/message_mods) +#define COMSIG_ITEM_TALK_INTO "item_talk_into" ///called from base of /obj/item/radio/proc/set_frequency(): (list/args) #define COMSIG_RADIO_NEW_FREQUENCY "radio_new_frequency" -///called from base of /obj/item/radio/proc/talk_into(): (atom/movable/M, message, channel) +///called from base of /obj/item/radio/talk_into(): (atom/movable/M, message, channel) #define COMSIG_RADIO_NEW_MESSAGE "radio_new_message" ///called from base of /obj/item/radio/proc/on_receive_messgae(): (list/data) #define COMSIG_RADIO_RECEIVE_MESSAGE "radio_receive_message" diff --git a/code/__DEFINES/economy.dm b/code/__DEFINES/economy.dm index 93b0678581ae8..32408e4f538e9 100644 --- a/code/__DEFINES/economy.dm +++ b/code/__DEFINES/economy.dm @@ -70,12 +70,13 @@ #define PAYMENT_CLINICAL "clinical" #define PAYMENT_FRIENDLY "friendly" #define PAYMENT_ANGRY "angry" +#define PAYMENT_VENDING "vending" #define MARKET_TREND_UPWARD 1 #define MARKET_TREND_DOWNWARD -1 #define MARKET_TREND_STABLE 0 -#define MARKET_EVENT_PROBABILITY 1 //Probability of a market event firing, in percent. Fires once per material, every 20 seconds. +#define MARKET_EVENT_PROBABILITY 8 //Probability of a market event firing, in percent. Fires once per material, every stock market tick. #define MARKET_PROFIT_MODIFIER 0.8 //We don't make every sale a 1-1 of the actual buy price value, like with real life taxes and to encourage more smart trades diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 0d2fb6b874d48..a4d826d87caf6 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -79,13 +79,6 @@ #define ui_navigate_menu "EAST-4:22,SOUTH:5" #define ui_floor_menu "EAST-4:14,SOUTH:37" -//Upper-middle right (alerts) -#define ui_alert1 "EAST-1:28,CENTER+5:27" -#define ui_alert2 "EAST-1:28,CENTER+4:25" -#define ui_alert3 "EAST-1:28,CENTER+3:23" -#define ui_alert4 "EAST-1:28,CENTER+2:21" -#define ui_alert5 "EAST-1:28,CENTER+1:19" - //Upper left (action buttons) #define ui_action_palette "WEST+0:23,NORTH-1:5" #define ui_action_palette_offset(north_offset) ("WEST+0:23,NORTH-[1+north_offset]:5") @@ -98,6 +91,7 @@ #define ui_health "EAST-1:28,CENTER-1:19" #define ui_internal "EAST-1:28,CENTER+1:21" #define ui_mood "EAST-1:28,CENTER:21" +#define ui_hunger "EAST-1:2,CENTER:21" #define ui_spacesuit "EAST-1:28,CENTER-4:14" #define ui_stamina "EAST-1:28,CENTER-3:14" diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm index 2509d685d35df..29a2c6dc07ec7 100644 --- a/code/__DEFINES/icon_smoothing.dm +++ b/code/__DEFINES/icon_smoothing.dm @@ -192,8 +192,13 @@ DEFINE_BITFIELD(smoothing_junction, list( #define SMOOTH_GROUP_CLEANABLE_DIRT S_OBJ(68) ///obj/effect/decal/cleanable/dirt -#define SMOOTH_GROUP_GAS_TANK S_OBJ(72) +#define SMOOTH_GROUP_GAS_TANK S_OBJ(69) +#define SMOOTH_GROUP_SPIDER_WEB S_OBJ(70) // /obj/structure/spider/stickyweb +#define SMOOTH_GROUP_SPIDER_WEB_WALL S_OBJ(71) // /obj/structure/spider/stickyweb/sealed +#define SMOOTH_GROUP_SPIDER_WEB_ROOF S_OBJ(72) // /obj/structure/spider/passage +#define SMOOTH_GROUP_SPIDER_WEB_WALL_TOUGH S_OBJ(73) // /obj/structure/spider/stickyweb/sealed/thick +#define SMOOTH_GROUP_SPIDER_WEB_WALL_MIRROR S_OBJ(74) // /obj/structure/spider/stickyweb/sealed/reflector /// Performs the work to set smoothing_groups and canSmoothWith. /// An inlined function used in both turf/Initialize and atom/Initialize. diff --git a/code/__DEFINES/procpath.dm b/code/__DEFINES/procpath.dm index 642ca3eab6cc8..16716d6c091f2 100644 --- a/code/__DEFINES/procpath.dm +++ b/code/__DEFINES/procpath.dm @@ -15,12 +15,12 @@ // below, their accesses are optimized away. /// A text string of the verb's name. - var/name as text + var/name = null as text|null /// The verb's help text or description. - var/desc as text + var/desc = null as text|null /// The category or tab the verb will appear in. - var/category as text + var/category = null as text|null /// Only clients/mobs with `see_invisibility` higher can use the verb. - var/invisibility as num + var/invisibility = null as num|null /// Whether or not the verb appears in statpanel and commandbar when you press space - var/hidden as num + var/hidden = null as num|null diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index ba22075285c68..018d7b26f2b39 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -55,11 +55,14 @@ #define MODE_MAFIA "mafia" +/// Applies singing characters to the message #define MODE_SING "sing" - +/// A custom say emote is being supplied [value = the emote] #define MODE_CUSTOM_SAY_EMOTE "custom_say" - +/// No message is following, just emote #define MODE_CUSTOM_SAY_ERASE_INPUT "erase_input" +/// Message is being relayed through another object +#define MODE_RELAY "relayed" //Spans. Robot speech, italics, etc. Applied in compose_message(). #define SPAN_ROBOT "robot" @@ -75,8 +78,11 @@ #define SPAN_HELIUM "small" //bitflag #defines for return value of the radio() proc. +/// Makes the message use italics #define ITALICS (1<<0) +/// Reduces the range of the message to 1 #define REDUCE_RANGE (1<<1) +/// Stops any actual message from being sent #define NOPASS (1<<2) /// Range to hear normal messages diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 8301a9fb107e8..5b0ea31dfbfd0 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -173,3 +173,4 @@ GLOBAL_LIST_INIT(announcer_keys, list( #define SFX_ROCK_TAP "rock_tap" #define SFX_SEAR "sear" #define SFX_REEL "reel" +#define SFX_RATTLE "rattle" diff --git a/code/__DEFINES/text.dm b/code/__DEFINES/text.dm index ffac885bf4c88..c303eebbcdc56 100644 --- a/code/__DEFINES/text.dm +++ b/code/__DEFINES/text.dm @@ -58,6 +58,12 @@ /// Removes everything enclose in < and > inclusive of the bracket, and limits the length of the message. #define STRIP_HTML_FULL(text, limit) (GLOB.html_tags.Replace(copytext(text, 1, limit), "")) +/** + * stuff like `copytext(input, length(input))` will trim the last character of the input, + * because DM does it so it copies until the char BEFORE the `end` arg, so we need to bump `end` by 1 in these cases. + */ +#define PREVENT_CHARACTER_TRIM_LOSS(integer) (integer + 1) + /// Folder directory for strings #define STRING_DIRECTORY "strings" diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 1935c3c0aee3d..6a2a5152903ba 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -39,6 +39,7 @@ #define PRIDE_WEEK "Pride Week" #define MOTH_WEEK "Moth Week" #define IAN_HOLIDAY "Ian's Birthday" +#define HOTDOG_DAY "National Hot Dog Day" /* Days of the week to make it easier to reference them. diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index dd237bf54b704..7f4fbd929367f 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -34,6 +34,8 @@ #define CULT_TRAIT "cult" #define LICH_TRAIT "lich" +#define VENDING_MACHINE_TRAIT "vending_machine" + #define ABSTRACT_ITEM_TRAIT "abstract-item" /// A trait given by any status effect #define STATUS_EFFECT_TRAIT "status-effect" diff --git a/code/__HELPERS/clients.dm b/code/__HELPERS/clients.dm index 3b61cf1e1c456..156f9e2b5dcdf 100644 --- a/code/__HELPERS/clients.dm +++ b/code/__HELPERS/clients.dm @@ -10,3 +10,9 @@ if (ch < 48 || ch > 57) //0-9 return FALSE return TRUE + +/// Proc that just logs whenever an uninitialized client tries to do something before they have fully gone through New(). +/// Intended to be used in conjunction with the `VALIDATE_CLIENT_INITIALIZATION()` macro, but can be dropped anywhere when we look at the `fully_created` var on /client. +/proc/unvalidated_client_error(client/target) + to_chat(target, span_warning("You are not fully initialized yet! Please wait a moment.")) + log_access("Client [key_name(target)] attempted to execute a verb before being fully initialized.") diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index f7889f0b5af80..f0741f3d8f3f2 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -258,3 +258,10 @@ GLOBAL_LIST_INIT(WALLITEMS_EXTERIOR, typecacheof(list( /obj/structure/camera_assembly, /obj/structure/light_construct, ))) + +/// A static typecache of all the money-based items that can be actively used as currency. +GLOBAL_LIST_INIT(allowed_money, typecacheof(list( + /obj/item/coin, + /obj/item/holochip, + /obj/item/stack/spacecash, +))) diff --git a/code/__HELPERS/logging/debug.dm b/code/__HELPERS/logging/debug.dm index c4ed2f1086f01..ad5670d2d119e 100644 --- a/code/__HELPERS/logging/debug.dm +++ b/code/__HELPERS/logging/debug.dm @@ -23,6 +23,9 @@ /proc/log_mapping(text, skip_world_log) #ifdef UNIT_TESTS GLOB.unit_test_mapping_logs += text +#endif +#ifdef MAP_TEST + message_admins("Mapping: [text]") #endif logger.Log(LOG_CATEGORY_DEBUG_MAPPING, text) if(skip_world_log) diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index c28357eb478b9..ead9d54ebaa5f 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -131,23 +131,23 @@ * Returns: [SI_COEFFICIENT = si unit coefficient, SI_UNIT = prefixed si unit.] */ /proc/siunit_isolated(value, unit, maxdecimals=1) - var/static/list/prefixes = list("f","p","n","μ","m","","k","M","G","T","P") + var/static/list/prefixes = list("q","r","y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y","R","Q") // We don't have prefixes beyond this point // and this also captures value = 0 which you can't compute the logarithm for // and also byond numbers are floats and doesn't have much precision beyond this point anyway - if(abs(value) <= 1e-18) + if(abs(value) < 1e-30) . = list(SI_COEFFICIENT = 0, SI_UNIT = " [unit]") return - var/exponent = clamp(log(10, abs(value)), -15, 15) // Calculate the exponent and clamp it so we don't go outside the prefix list bounds + var/exponent = clamp(log(10, abs(value)), -30, 30) // Calculate the exponent and clamp it so we don't go outside the prefix list bounds var/divider = 10 ** (round(exponent / 3) * 3) // Rounds the exponent to nearest SI unit and power it back to the full form var/coefficient = round(value / divider, 10 ** -maxdecimals) // Calculate the coefficient and round it to desired decimals - var/prefix_index = round(exponent / 3) + 6 // Calculate the index in the prefixes list for this exponent + var/prefix_index = round(exponent / 3) + 11 // Calculate the index in the prefixes list for this exponent // An edge case which happens if we round 999.9 to 0 decimals for example, which gets rounded to 1000 // In that case, we manually swap up to the next prefix if there is one available - if(coefficient >= 1000 && prefix_index < 11) + if(coefficient >= 1000 && prefix_index < 21) coefficient /= 1e3 prefix_index++ diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm index 5eb4bda14e784..87d4348580e29 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 515 -#define MIN_COMPILER_BUILD 1621 +#define MIN_COMPILER_BUILD 1627 #if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) //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 515.1621 or higher +#error You need version 515.1627 or higher #endif // Keep savefile compatibilty at minimum supported level diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 2a4854c37b858..8768a1e36222b 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -142,3 +142,12 @@ #warn In order to build, run BUILD.bat in the root directory. #warn Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build. #endif + +/// Runs the game in "map test mode" +/// Map test mode prevents common annoyances, such as rats from spawning and random light fixture breakage, +/// so mappers can test important facets of their map (working powernet, atmos, good light coverage) without these interfering. +// #define MAP_TEST + +#ifdef MAP_TEST +#warn Compiling in MAP_TEST mode. Certain game mechanics will be disabled. +#endif diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 4135428194b8f..37e7ef30d41c7 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -313,7 +313,8 @@ DEFINE_BITFIELD(resistance_flags, list( "UNACIDABLE" = UNACIDABLE, "ACID_PROOF" = ACID_PROOF, "INDESTRUCTIBLE" = INDESTRUCTIBLE, - "FREEZE_PROOF" = FREEZE_PROOF + "FREEZE_PROOF" = FREEZE_PROOF, + "SHUTTLE_CRUSH_PROOF" = SHUTTLE_CRUSH_PROOF )) DEFINE_BITFIELD(sight, list( diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 1f5f7588162ab..8b96a5491bd47 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -68,8 +68,8 @@ if(client && hud_used) hud_used.reorganize_alerts() if(!no_anim) - thealert.transform = matrix(32, 6, MATRIX_TRANSLATE) - animate(thealert, transform = matrix(), time = 2.5, easing = CUBIC_EASING) + thealert.transform = matrix(32, 0, MATRIX_TRANSLATE) + animate(thealert, transform = matrix(), time = 1 SECONDS, easing = ELASTIC_EASING) if(timeout_override) thealert.timeout = timeout_override if(thealert.timeout) @@ -185,22 +185,6 @@ //End gas alerts - -/atom/movable/screen/alert/fat - name = "Fat" - desc = "You ate too much food, lardass. Run around the station and lose some weight." - icon_state = "fat" - -/atom/movable/screen/alert/hungry - name = "Hungry" - desc = "Some food would be good right about now." - icon_state = "hungry" - -/atom/movable/screen/alert/starving - name = "Starving" - desc = "You're severely malnourished. The hunger pains make moving around a chore." - icon_state = "starving" - /atom/movable/screen/alert/gross name = "Grossed out." desc = "That was kind of gross..." @@ -887,7 +871,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." set_never_round() return if(LAZYACCESS(modifiers, CTRL_CLICK) && poll.jump_to_me) - jump_to_pic_source() + jump_to_jump_target() return handle_sign_up() @@ -907,7 +891,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." poll.undo_never_for_this_round(owner) color = initial(color) -/atom/movable/screen/alert/poll_alert/proc/jump_to_pic_source() +/atom/movable/screen/alert/poll_alert/proc/jump_to_jump_target() if(!poll?.jump_to_me || !isobserver(owner)) return var/turf/target_turf = get_turf(poll.jump_to_me) @@ -921,7 +905,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." if(href_list["signup"]) handle_sign_up() if(href_list["jump"]) - jump_to_pic_source() + jump_to_jump_target() return /atom/movable/screen/alert/poll_alert/proc/update_signed_up_overlay() @@ -1034,39 +1018,34 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." // PRIVATE = only edit, use, or override these if you're editing the system as a whole +/// Gets the placement for the alert based on its index +/datum/hud/proc/get_ui_alert_placement(index) + // Only has support for 5 slots currently + if(index > 5) + return "" + + return "EAST-1:28,CENTER+[6 - index]:[29 - (index * 2)]" + // Re-render all alerts - also called in /datum/hud/show_hud() because it's needed there /datum/hud/proc/reorganize_alerts(mob/viewmob) var/mob/screenmob = viewmob || mymob if(!screenmob.client) - return + return FALSE var/list/alerts = mymob.alerts if(!hud_shown) for(var/i in 1 to alerts.len) screenmob.client.screen -= alerts[alerts[i]] - return 1 - for(var/i in 1 to alerts.len) + return TRUE + for(var/i in 1 to length(alerts)) var/atom/movable/screen/alert/alert = alerts[alerts[i]] if(alert.icon_state == "template") alert.icon = ui_style - switch(i) - if(1) - . = ui_alert1 - if(2) - . = ui_alert2 - if(3) - . = ui_alert3 - if(4) - . = ui_alert4 - if(5) - . = ui_alert5 // Right now there's 5 slots - else - . = "" - alert.screen_loc = . + alert.screen_loc = get_ui_alert_placement(i) screenmob.client.screen |= alert if(!viewmob) - for(var/M in mymob.observers) - reorganize_alerts(M) - return 1 + for(var/viewer in mymob.observers) + reorganize_alerts(viewer) + return TRUE /atom/movable/screen/alert/Click(location, control, params) if(!usr || !usr.client) diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index b915e3dc19c86..0b907833f76c7 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -95,6 +95,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( var/atom/movable/screen/stamina var/atom/movable/screen/healthdoll var/atom/movable/screen/spacesuit + var/atom/movable/screen/hunger // subtypes can override this to force a specific UI style var/ui_style @@ -239,6 +240,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( stamina = null healthdoll = null spacesuit = null + hunger = null blobpwrdisplay = null alien_plasma_display = null alien_queen_finder = null diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index b12ade0c58d43..5834a3973555c 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -272,6 +272,9 @@ healths = new /atom/movable/screen/healths(null, src) infodisplay += healths + hunger = new /atom/movable/screen/hunger(null, src) + infodisplay += hunger + healthdoll = new /atom/movable/screen/healthdoll(null, src) infodisplay += healthdoll diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index dfb5f072d896c..dcf7e230906e3 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -750,3 +750,102 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/splash) name = "stamina" icon_state = "stamina0" screen_loc = ui_stamina + +#define HUNGER_STATE_FAT 2 +#define HUNGER_STATE_FULL 1 +#define HUNGER_STATE_FINE 0 +#define HUNGER_STATE_HUNGRY -1 +#define HUNGER_STATE_STARVING -2 + +/atom/movable/screen/hunger + name = "hunger" + icon_state = "hungerbar" + base_icon_state = "hungerbar" + screen_loc = ui_hunger + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + /// What state of hunger are we in? + VAR_PRIVATE/state = HUNGER_STATE_FINE + /// What food icon do we show by the bar + var/food_icon = 'icons/obj/food/burgerbread.dmi' + /// What food icon state do we show by the bar + var/food_icon_state = "hburger" + /// The image shown by the bar. + VAR_PRIVATE/image/food_image + +/atom/movable/screen/hunger/Initialize(mapload, datum/hud/hud_owner) + . = ..() + var/mob/living/hungry = hud_owner?.mymob + if(!istype(hungry)) + return + + if(!ishuman(hungry) || CONFIG_GET(flag/disable_human_mood)) + screen_loc = ui_mood // Slot in where mood normally is if mood is disabled + + food_image = image(icon = food_icon, icon_state = food_icon_state, pixel_x = -5) + food_image.plane = plane + food_image.appearance_flags |= KEEP_APART // To be unaffected by filters applied to src + food_image.add_filter("simple_outline", 2, outline_filter(1, COLOR_BLACK)) + underlays += food_image // To be below filters applied to src + + SetInvisibility(INVISIBILITY_ABSTRACT, name) // Start invisible, update later + update_appearance() + +/atom/movable/screen/hunger/proc/update_hunger_state() + var/mob/living/hungry = hud?.mymob + if(!istype(hungry)) + return + + if(HAS_TRAIT(hungry, TRAIT_NOHUNGER) || !hungry.get_organ_slot(ORGAN_SLOT_STOMACH)) + state = HUNGER_STATE_FINE + return + + if(HAS_TRAIT(hungry, TRAIT_FAT)) + state = HUNGER_STATE_FAT + return + + switch(hungry.nutrition) + if(NUTRITION_LEVEL_FULL to INFINITY) + state = HUNGER_STATE_FULL + if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL) + state = HUNGER_STATE_FINE + if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) + state = HUNGER_STATE_HUNGRY + if(0 to NUTRITION_LEVEL_STARVING) + state = HUNGER_STATE_STARVING + +/atom/movable/screen/hunger/update_appearance(updates) + var/old_state = state + update_hunger_state() // Do this before we call all the other update procs + . = ..() + if(state == old_state) // Let's not be wasteful + return + if(state == HUNGER_STATE_FINE) + SetInvisibility(INVISIBILITY_ABSTRACT, name) + return + + else if(invisibility) + RemoveInvisibility(name) + + if(state == HUNGER_STATE_STARVING) + if(!get_filter("hunger_outline")) + add_filter("hunger_outline", 1, list("type" = "outline", "color" = "#FF0033", "alpha" = 0, "size" = 2)) + animate(get_filter("hunger_outline"), alpha = 200, time = 1.5 SECONDS, loop = -1) + animate(alpha = 0, time = 1.5 SECONDS) + + else if(get_filter("hunger_outline")) + remove_filter("hunger_outline") + + // Update color of the food + underlays -= food_image + food_image.color = state == HUNGER_STATE_FAT ? COLOR_DARK : null + underlays += food_image + +/atom/movable/screen/hunger/update_icon_state() + . = ..() + icon_state = "[base_icon_state][state]" + +#undef HUNGER_STATE_FAT +#undef HUNGER_STATE_FULL +#undef HUNGER_STATE_FINE +#undef HUNGER_STATE_HUNGRY +#undef HUNGER_STATE_STARVING diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index d9a0118a06ded..72ed2cabb6936 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -264,6 +264,7 @@ user.changeNext_move(attack_speed) user.do_attack_animation(attacked_atom) attacked_atom.attacked_by(src, user) + SEND_SIGNAL(src, COMSIG_ITEM_POST_ATTACK_ATOM, attacked_atom, user) /// Called from [/obj/item/proc/attack_atom] and [/obj/item/proc/attack] if the attack succeeds /atom/proc/attacked_by(obj/item/attacking_item, mob/living/user) diff --git a/code/controllers/subsystem/bitrunning.dm b/code/controllers/subsystem/bitrunning.dm new file mode 100644 index 0000000000000..2b303911e42c9 --- /dev/null +++ b/code/controllers/subsystem/bitrunning.dm @@ -0,0 +1,60 @@ +#define REDACTED "???" + +SUBSYSTEM_DEF(bitrunning) + name = "Bitrunning" + flags = SS_NO_FIRE + + var/list/all_domains = list() + +/datum/controller/subsystem/bitrunning/Initialize() + InitializeDomains() + return SS_INIT_SUCCESS + +/datum/controller/subsystem/bitrunning/proc/InitializeDomains() + for(var/path in subtypesof(/datum/lazy_template/virtual_domain)) + all_domains += new path() + +/// Compiles a list of available domains. +/datum/controller/subsystem/bitrunning/proc/get_available_domains(scanner_tier, points) + var/list/levels = list() + + for(var/datum/lazy_template/virtual_domain/domain as anything in all_domains) + if(domain.test_only) + continue + var/can_view = domain.difficulty < scanner_tier && domain.cost <= points + 5 + var/can_view_reward = domain.difficulty < (scanner_tier + 1) && domain.cost <= points + 3 + + levels += list(list( + "cost" = domain.cost, + "desc" = can_view ? domain.desc : "Limited scanning capabilities. Cannot infer domain details.", + "difficulty" = domain.difficulty, + "id" = domain.key, + "is_modular" = domain.is_modular, + "has_secondary_objectives" = assoc_value_sum(domain.secondary_loot) ? TRUE : FALSE, + "name" = can_view ? domain.name : REDACTED, + "reward" = can_view_reward ? domain.reward_points : REDACTED, + )) + + return levels + +/datum/controller/subsystem/bitrunning/proc/pick_secondary_loot(completed_domain) + var/datum/lazy_template/virtual_domain/domain = completed_domain + var/choice + + if(assoc_value_sum(domain.secondary_loot)) + choice = pick_weight(domain.secondary_loot) + domain.secondary_loot[choice] -= 1 + else + choice = /obj/item/paper/paperslip/bitrunning_error + CRASH("Virtual domain [domain.name] tried to pick secondary objective loot, but secondary_loot list was empty.") + return choice + +/obj/item/paper/paperslip/bitrunning_error + name = "Apology Letter" + desc = "Something went wrong here." + +/obj/item/paper/paperslip/bitrunning_error/Initialize(mapload) + default_raw_text = "Your reward for collecting the encrypted curiosity failed to arrive, please report this to technical support." + return ..() + +#undef REDACTED diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm index 15c205b6748f0..b893ee701844a 100644 --- a/code/controllers/subsystem/dbcore.dm +++ b/code/controllers/subsystem/dbcore.dm @@ -1,3 +1,4 @@ +#define SHUTDOWN_QUERY_TIMELIMIT (1 MINUTES) SUBSYSTEM_DEF(dbcore) name = "Database" flags = SS_TICKER @@ -6,12 +7,17 @@ SUBSYSTEM_DEF(dbcore) init_order = INIT_ORDER_DBCORE priority = FIRE_PRIORITY_DATABASE - var/failed_connection_timeout = 0 - var/schema_mismatch = 0 var/db_minor = 0 var/db_major = 0 + /// Number of failed connection attempts this try. Resets after the timeout or successful connection var/failed_connections = 0 + /// Max number of consecutive failures before a timeout (here and not a define so it can be vv'ed mid round if needed) + var/max_connection_failures = 5 + /// world.time that connection attempts can resume + var/failed_connection_timeout = 0 + /// Total number of times connections have had to be timed out. + var/failed_connection_timeout_count = 0 var/last_error @@ -174,18 +180,27 @@ SUBSYSTEM_DEF(dbcore) /datum/controller/subsystem/dbcore/Shutdown() shutting_down = TRUE - to_chat(world, span_boldannounce("Clearing DB queries standby:[length(queries_standby)] active: [length(queries_active)] all: [length(all_queries)]")) + var/msg = "Clearing DB queries standby:[length(queries_standby)] active: [length(queries_active)] all: [length(all_queries)]" + to_chat(world, span_boldannounce(msg)) + log_world(msg) //This is as close as we can get to the true round end before Disconnect() without changing where it's called, defeating the reason this is a subsystem + var/endtime = REALTIMEOFDAY + SHUTDOWN_QUERY_TIMELIMIT if(SSdbcore.Connect()) - //Execute all waiting queries + //Take over control of all active queries + var/queries_to_check = queries_active.Copy() + queries_active.Cut() + + //Start all waiting queries for(var/datum/db_query/query in queries_standby) - run_query_sync(query) + run_query(query) + queries_to_check += query queries_standby -= query - for(var/datum/db_query/query in queries_active) - //Finish any remaining active qeries - UNTIL(query.process()) - queries_active -= query - + + //wait for them all to finish + for(var/datum/db_query/query in queries_to_check) + UNTIL(query.process() || REALTIMEOFDAY > endtime) + + //log shutdown to the db var/datum/db_query/query_round_shutdown = SSdbcore.NewQuery( "UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = :end_state WHERE id = :round_id", list("end_state" = SSticker.end_state, "round_id" = GLOB.round_id), @@ -194,7 +209,9 @@ SUBSYSTEM_DEF(dbcore) query_round_shutdown.Execute(FALSE) qdel(query_round_shutdown) - to_chat(world, span_boldannounce("Done clearing DB queries standby:[length(queries_standby)] active: [length(queries_active)] all: [length(all_queries)]")) + msg = "Done clearing DB queries standby:[length(queries_standby)] active: [length(queries_active)] all: [length(all_queries)]" + to_chat(world, span_boldannounce(msg)) + log_world(msg) if(IsConnected()) Disconnect() stop_db_daemon() @@ -230,12 +247,16 @@ SUBSYSTEM_DEF(dbcore) /datum/controller/subsystem/dbcore/proc/Connect() if(IsConnected()) return TRUE + + if(connection) + Disconnect() //clear the current connection handle so isconnected() calls stop invoking rustg + connection = null //make sure its cleared even if runtimes happened - if(failed_connection_timeout <= world.time) //it's been more than 5 seconds since we failed to connect, reset the counter + if(failed_connection_timeout <= world.time) //it's been long enough since we failed to connect, reset the counter failed_connections = 0 + failed_connection_timeout = 0 - if(failed_connections > 5) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect for 5 seconds. - failed_connection_timeout = world.time + 50 + if(failed_connection_timeout > 0) return FALSE if(!CONFIG_GET(flag/sql_enabled)) @@ -271,6 +292,11 @@ SUBSYSTEM_DEF(dbcore) last_error = result["data"] log_sql("Connect() failed | [last_error]") ++failed_connections + //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect for a time. + if(failed_connections > max_connection_failures) + failed_connection_timeout_count++ + //basic exponential backoff algorithm + failed_connection_timeout = world.time + ((2 ** failed_connection_timeout_count) SECONDS) /datum/controller/subsystem/dbcore/proc/CheckSchemaVersion() if(CONFIG_GET(flag/sql_enabled)) @@ -650,3 +676,4 @@ Ignore_errors instructes mysql to continue inserting rows if some of them have e /datum/db_query/proc/Close() rows = null item = null +#undef SHUTDOWN_QUERY_TIMELIMIT diff --git a/code/controllers/subsystem/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm index f7c37cb41e3f7..aca8ff1fc9e7e 100644 --- a/code/controllers/subsystem/dynamic/dynamic.dm +++ b/code/controllers/subsystem/dynamic/dynamic.dm @@ -359,6 +359,7 @@ SUBSYSTEM_DEF(dynamic) . += "
Additional Notes:

" + footnote_pile +#ifndef MAP_TEST print_command_report(., "[command_name()] Status Summary", announce=FALSE) if(greenshift) priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no credible threats to [station_name()]. All station construction projects have been authorized. Have a secure shift!", "Security Report", SSstation.announcer.get_rand_report_sound(), color_override = "green") @@ -366,6 +367,7 @@ SUBSYSTEM_DEF(dynamic) if(SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_BLUE) SSsecurity_level.set_level(SEC_LEVEL_BLUE, announce = FALSE) priority_announce("[SSsecurity_level.current_security_level.elevating_to_announcement]\n\nA summary has been copied and printed to all communications consoles.", "Security level elevated.", ANNOUNCER_INTERCEPT, color_override = SSsecurity_level.current_security_level.announcement_color) +#endif return . diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm index 337d9490b9431..e4c580f535abb 100644 --- a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm +++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm @@ -136,11 +136,11 @@ SSdynamic.log_dynamic_and_announce("Polling [possible_volunteers.len] players to apply for the [name] ruleset.") candidates = SSpolling.poll_ghost_candidates( - question = "Looking for volunteers to become [antag_flag] for [name]", + question = "Looking for volunteers to become [span_notice(antag_flag)] for [span_danger(name)]", check_jobban = antag_flag_override, role = antag_flag || antag_flag_override, poll_time = 30 SECONDS, - pic_source = signup_item_path, + alert_pic = signup_item_path, role_name_text = antag_flag, ) diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 12eadcc5fe735..362129f130570 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -50,7 +50,11 @@ SUBSYSTEM_DEF(events) //checks if we should select a random event yet, and reschedules if necessary /datum/controller/subsystem/events/proc/checkEvent() if(scheduled <= world.time) +#ifdef MAP_TEST + message_admins("Random event skipped (Game is compiled in MAP_TEST mode)") +#else spawnEvent() +#endif reschedule() //decides which world.time we should select another random event at. diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index c5df419d735ba..0dbfa267882c8 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -160,7 +160,10 @@ SUBSYSTEM_DEF(job) continue new_all_occupations += job name_occupations[job.title] = job + for(var/alt_title in job.alternate_titles) + name_occupations[alt_title] = job type_occupations[job_type] = job + if(job.job_flags & JOB_NEW_PLAYER_JOINABLE) new_joinable_occupations += job if(!LAZYLEN(job.departments_list)) diff --git a/code/controllers/subsystem/minor_mapping.dm b/code/controllers/subsystem/minor_mapping.dm index 565f13b6b017f..6acbbc1894e25 100644 --- a/code/controllers/subsystem/minor_mapping.dm +++ b/code/controllers/subsystem/minor_mapping.dm @@ -7,13 +7,15 @@ SUBSYSTEM_DEF(minor_mapping) flags = SS_NO_FIRE /datum/controller/subsystem/minor_mapping/Initialize() - #ifdef UNIT_TESTS // This whole subsystem just introduces a lot of odd confounding variables into unit test situations, so let's just not bother with doing an initialize here. +// This whole subsystem just introduces a lot of odd confounding variables into unit test situations, +// so let's just not bother with doing an initialize here. +#if defined(MAP_TEST) || defined(UNIT_TESTS) return SS_INIT_NO_NEED - #else +#else trigger_migration(CONFIG_GET(number/mice_roundstart)) place_satchels(satchel_amount = 2) return SS_INIT_SUCCESS - #endif // the mice are easily the bigger problem, but let's just avoid anything that could cause some bullshit. +#endif /// Spawns some critters on exposed wires, usually but not always mice /datum/controller/subsystem/minor_mapping/proc/trigger_migration(to_spawn=10) diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm index 8f865e5d09871..36679fa1d2a02 100644 --- a/code/controllers/subsystem/persistence/_persistence.dm +++ b/code/controllers/subsystem/persistence/_persistence.dm @@ -37,6 +37,14 @@ SUBSYSTEM_DEF(persistence) /// Will be null'd once the persistence system initializes, and never read from again. var/list/obj/item/storage/photo_album/queued_photo_albums + /// A json_database to data/piggy banks.json + /// Schema is persistence_id => array of coins, space cash and holochips. + var/datum/json_database/piggy_banks_database + /// List of persistene ids which piggy banks. + var/list/queued_broken_piggy_ids + + var/list/broken_piggy_banks + var/rounds_since_engine_exploded = 0 var/delam_highscore = 0 var/tram_hits_this_round = 0 diff --git a/code/controllers/subsystem/persistence/piggy_banks.dm b/code/controllers/subsystem/persistence/piggy_banks.dm new file mode 100644 index 0000000000000..240fd98ab0c9e --- /dev/null +++ b/code/controllers/subsystem/persistence/piggy_banks.dm @@ -0,0 +1,56 @@ +///This proc is used to initialize holochips, cash and coins inside our persistent piggy bank. +/datum/controller/subsystem/persistence/proc/load_piggy_bank(obj/item/piggy_bank/piggy) + if(isnull(piggy_banks_database)) + piggy_banks_database = new("data/piggy_banks.json") + + var/list/data = piggy_banks_database.get_key(piggy.persistence_id) + if(isnull(data)) + return + var/total_value = 0 + for(var/iteration in 1 to length(data)) + var/money_path = text2path(data[iteration]) + if(!money_path) //For a reason or another, it was removed. + continue + var/obj/item/spawned + if(ispath(money_path, /obj/item/holochip)) + //We want to safely access the assoc of this position and not that of last key that happened to match this one. + var/list/key_and_assoc = data.Copy(iteration, iteration + 1) + var/amount = key_and_assoc["[money_path]"] + spawned = new money_path (piggy, amount) + //the operations are identical to those of chips, but they're different items, so I'll keep them separated. + else if(ispath(money_path, /obj/item/stack/spacecash)) + var/list/key_and_assoc = data.Copy(iteration, iteration + 1) + var/amount = key_and_assoc["[money_path]"] + spawned = new money_path (piggy, amount) + else if(ispath(money_path, /obj/item/coin)) + spawned = new money_path (piggy) + else + stack_trace("Unsupported path found in the data of a persistent piggy bank. item: [money_path], id:[piggy.persistence_id]") + continue + total_value += spawned.get_item_credit_value() + if(total_value >= piggy.maximum_value) + break + +///This proc is used to save money stored inside our persistent the piggy bank for the next time it's loaded. +/datum/controller/subsystem/persistence/proc/save_piggy_bank(obj/item/piggy_bank/piggy) + if(isnull(piggy_banks_database)) + return + + if(queued_broken_piggy_ids) + for(var/broken_id in queued_broken_piggy_ids) + piggy_banks_database.remove(broken_id) + queued_broken_piggy_ids = null + + var/list/data = list() + for(var/obj/item/item as anything in piggy.contents) + var/piggy_value = 1 + if(istype(item, /obj/item/holochip)) + var/obj/item/holochip/chip = item + piggy_value = chip.credits + else if(istype(item, /obj/item/stack/spacecash)) + var/obj/item/stack/spacecash/cash = item + piggy_value = cash.amount + else if(!istype(item, /obj/item/coin)) + continue + data += list("[item.type]" = piggy_value) + piggy_banks_database.set_key(piggy.persistence_id, data) diff --git a/code/controllers/subsystem/polling.dm b/code/controllers/subsystem/polling.dm index 3fd8bcc125e5c..1f748806d0041 100644 --- a/code/controllers/subsystem/polling.dm +++ b/code/controllers/subsystem/polling.dm @@ -27,10 +27,14 @@ SUBSYSTEM_DEF(polling) * * ignore_category: Optional, A poll category. If a candidate has this category in their ignore list, they won't be polled. * * flash_window: If TRUE, the candidate's window will flash when they're polled. * * list/group: A list of candidates to poll. - * * pic_source: Optional, An /atom or an /image to display on the poll alert. + * * alert_pic: Optional, An /atom or an /image to display on the poll alert. + * * jump_target: An /atom to teleport/jump to, if alert_pic is an /atom defaults to that. * * role_name_text: Optional, A string to display in logging / the (default) question. If null, the role name will be used. * * list/custom_response_messages: Optional, A list of strings to use as responses to the poll. If null, the default responses will be used. see __DEFINES/polls.dm for valid keys to use. * * start_signed_up: If TRUE, all candidates will start signed up for the poll, making it opt-out rather than opt-in. + * * amount_to_pick: Lets you pick candidates and return a single mob or list of mobs that were chosen. + * * chat_text_border_icon: Object or path to make an icon of to decorate the chat announcement. + * * announce_chosen: Whether we should announce the chosen candidates in chat. This is ignored unless amount_to_pick is greater than 0. * * Returns a list of all mobs who signed up for the poll. */ @@ -42,18 +46,21 @@ SUBSYSTEM_DEF(polling) ignore_category = null, flash_window = TRUE, list/group = null, - pic_source, + alert_pic, + jump_target, role_name_text, list/custom_response_messages, start_signed_up = FALSE, + amount_to_pick = 0, + chat_text_border_icon, + announce_chosen = TRUE, ) - RETURN_TYPE(/list/mob) if(group.len == 0) - return list() + return if(role && !role_name_text) role_name_text = role if(role_name_text && !question) - question = "Do you want to play as [full_capitalize(role_name_text)]?" + question = "Do you want to play as [span_notice(role_name_text)]?" if(!question) question = "Do you want to play as a special role?" log_game("Polling candidates [role_name_text ? "for [role_name_text]" : "\"[question]\""] for [DisplayTimeText(poll_time)] seconds") @@ -61,9 +68,10 @@ SUBSYSTEM_DEF(polling) // Start firing total_polls++ - var/jumpable = isatom(pic_source) ? pic_source : null + if(!jump_target && isatom(alert_pic)) + jump_target = alert_pic - var/datum/candidate_poll/new_poll = new(role_name_text, question, poll_time, ignore_category, jumpable, custom_response_messages) + var/datum/candidate_poll/new_poll = new(role_name_text, question, poll_time, ignore_category, jump_target, custom_response_messages) LAZYADD(currently_polling, new_poll) var/category = "[new_poll.poll_key]_poll_alert" @@ -121,83 +129,130 @@ SUBSYSTEM_DEF(polling) break // Image to display - var/image/poll_image - if(pic_source) - if(!ispath(pic_source)) - var/atom/the_pic_source = pic_source - var/old_layer = the_pic_source.layer - var/old_plane = the_pic_source.plane - the_pic_source.plane = poll_alert_button.plane - the_pic_source.layer = FLOAT_LAYER - poll_alert_button.add_overlay(the_pic_source) - the_pic_source.layer = old_layer - the_pic_source.plane = old_plane + var/image/poll_image = image('icons/effects/effects.dmi', icon_state = "static") + if(alert_pic) + if(!ispath(alert_pic)) + var/mutable_appearance/picture_source = alert_pic + poll_image = picture_source else - poll_image = image(pic_source, layer = FLOAT_LAYER) - else - // Just use a generic image - poll_image = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER) + poll_image = image(alert_pic) if(poll_image) + poll_image.layer = FLOAT_LAYER poll_image.plane = poll_alert_button.plane poll_alert_button.add_overlay(poll_image) // Chat message var/act_jump = "" - if(isatom(pic_source) && isobserver(candidate_mob)) - act_jump = "\[Teleport\]" - var/act_signup = "\[[start_signed_up ? "Opt out" : "Sign Up"]\]" + var/custom_link_style_start = "" + var/custom_link_style_end = "style='color:DodgerBlue;font-weight:bold;-dm-text-outline: 1px black'" + if(isatom(alert_pic) && isobserver(candidate_mob)) + act_jump = "[custom_link_style_start]\[Teleport\]" + var/act_signup = "[custom_link_style_start]\[[start_signed_up ? "Opt out" : "Sign Up"]\]" var/act_never = "" if(ignore_category) - act_never = "\[Never For This Round\]" + act_never = "[custom_link_style_start]\[Never For This Round\]" if(!duplicate_message_check(alert_poll)) //Only notify people once. They'll notice if there are multiple and we don't want to spam people. SEND_SOUND(candidate_mob, 'sound/misc/notice2.ogg') - to_chat(candidate_mob, span_boldnotice(examine_block("Now looking for candidates [role_name_text ? "to play as \an [role_name_text]." : "\"[question]\""] [act_jump] [act_signup] [act_never]"))) + var/surrounding_icon + if(chat_text_border_icon) + var/image/surrounding_image + if(!ispath(chat_text_border_icon)) + var/mutable_appearance/border_image = chat_text_border_icon + surrounding_image = border_image + else + surrounding_image = image(chat_text_border_icon) + surrounding_icon = icon2html(surrounding_image, candidate_mob, extra_classes = "bigicon") + var/final_message = examine_block("[surrounding_icon] [span_ooc(question)] [surrounding_icon]\n[act_jump] [act_signup] [act_never]") + to_chat(candidate_mob, final_message) // Start processing it so it updates visually the timer START_PROCESSING(SSprocessing, poll_alert_button) // Sleep until the time is up UNTIL(new_poll.finished) - return new_poll.signed_up - -/datum/controller/subsystem/polling/proc/poll_ghost_candidates(question, role, check_jobban, poll_time = 30 SECONDS, ignore_category = null, flashwindow = TRUE, pic_source, role_name_text) + if(!(amount_to_pick > 0)) + return new_poll.signed_up + for(var/pick in 1 to amount_to_pick) + new_poll.chosen_candidates += pick_n_take(new_poll.signed_up) + if(announce_chosen) + new_poll.announce_chosen(group) + if(new_poll.chosen_candidates.len == 1) + var/chosen_one = pick(new_poll.chosen_candidates) + return chosen_one + return new_poll.chosen_candidates + +/datum/controller/subsystem/polling/proc/poll_ghost_candidates( + question, + role, + check_jobban, + poll_time = 30 SECONDS, + ignore_category = null, + flashwindow = TRUE, + alert_pic, + jump_target, + role_name_text, + list/custom_response_messages, + start_signed_up = FALSE, + amount_to_pick = 0, + chat_text_border_icon, + announce_chosen = TRUE, +) var/list/candidates = list() if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE)) - return candidates - + return for(var/mob/dead/observer/ghost_player in GLOB.player_list) candidates += ghost_player + return poll_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, candidates, alert_pic, jump_target, role_name_text, custom_response_messages, start_signed_up, amount_to_pick, chat_text_border_icon, announce_chosen) - return poll_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, candidates, pic_source, role_name_text) - -/datum/controller/subsystem/polling/proc/poll_ghost_candidates_for_mob(question, role, check_jobban, poll_time = 30 SECONDS, mob/target_mob, ignore_category = null, flashwindow = TRUE, pic_source, role_name_text) - var/static/list/mob/currently_polling_mobs = list() - - if(currently_polling_mobs.Find(target_mob)) - return list() - - currently_polling_mobs += target_mob - - var/list/possible_candidates = poll_ghost_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, pic_source, role_name_text) - - currently_polling_mobs -= target_mob - if(!target_mob || QDELETED(target_mob) || !target_mob.loc) - return list() - - return possible_candidates - -/datum/controller/subsystem/polling/proc/poll_ghost_candidates_for_mobs(question, role, check_jobban, poll_time = 30 SECONDS, list/mobs, ignore_category = null, flashwindow = TRUE, pic_source, role_name_text) - var/list/candidate_list = poll_ghost_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, pic_source, role_name_text) - - for(var/mob/potential_mob as anything in mobs) - if(QDELETED(potential_mob) || !potential_mob.loc) - mobs -= potential_mob - - if(!length(mobs)) +/datum/controller/subsystem/polling/proc/poll_ghosts_for_target( + question, + role, + check_jobban, + poll_time = 30 SECONDS, + atom/movable/checked_target, + ignore_category = null, + flashwindow = TRUE, + alert_pic, + jump_target, + role_name_text, + list/custom_response_messages, + start_signed_up = FALSE, + chat_text_border_icon, + announce_chosen = TRUE, +) + var/static/list/atom/movable/currently_polling_targets = list() + if(currently_polling_targets.Find(checked_target)) + return + currently_polling_targets += checked_target + var/mob/chosen_one = poll_ghost_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, alert_pic, jump_target, role_name_text, custom_response_messages, start_signed_up, amount_to_pick = 1, chat_text_border_icon = chat_text_border_icon, announce_chosen = announce_chosen) + currently_polling_targets -= checked_target + if(!checked_target || QDELETED(checked_target) || !checked_target.loc) + return null + return chosen_one + +/datum/controller/subsystem/polling/proc/poll_ghosts_for_targets( + question, + role, + check_jobban, + poll_time = 30 SECONDS, + list/checked_targets, + ignore_category = null, + flashwindow = TRUE, + alert_pic, + jump_target, + role_name_text, + list/custom_response_messages, + start_signed_up = FALSE, + chat_text_border_icon, +) + var/list/candidate_list = poll_ghost_candidates(question, role, check_jobban, poll_time, ignore_category, flashwindow, alert_pic, jump_target, role_name_text, custom_response_messages, start_signed_up, chat_text_border_icon = chat_text_border_icon) + for(var/atom/movable/potential_target as anything in checked_targets) + if(QDELETED(potential_target) || !potential_target.loc) + checked_targets -= potential_target + if(!length(checked_targets)) return list() - return candidate_list /datum/controller/subsystem/polling/proc/is_eligible(mob/potential_candidate, role, check_jobban, the_ignore_category) diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm index 34c4a00f81d4c..2ac23cf702f27 100644 --- a/code/controllers/subsystem/processing/station.dm +++ b/code/controllers/subsystem/processing/station.dm @@ -118,6 +118,12 @@ PROCESSING_SUBSYSTEM_DEF(station) var/neutral_trait_budget = text2num(pick_weight(CONFIG_GET(keyed_list/neutral_station_traits))) var/negative_trait_budget = text2num(pick_weight(CONFIG_GET(keyed_list/negative_station_traits))) +#ifdef MAP_TEST + positive_trait_budget = 0 + neutral_trait_budget = 0 + negative_trait_budget = 0 +#endif + pick_traits(STATION_TRAIT_POSITIVE, positive_trait_budget) pick_traits(STATION_TRAIT_NEUTRAL, neutral_trait_budget) pick_traits(STATION_TRAIT_NEGATIVE, negative_trait_budget) diff --git a/code/controllers/subsystem/queuelinks.dm b/code/controllers/subsystem/queuelinks.dm index 6a3b828882162..a6d56cf622ec9 100644 --- a/code/controllers/subsystem/queuelinks.dm +++ b/code/controllers/subsystem/queuelinks.dm @@ -18,16 +18,30 @@ SUBSYSTEM_DEF(queuelinks) if(isnull(id)) CRASH("Attempted to add to queue with no ID; [what]") - var/datum/queue_link/link - if(isnull(queues[id])) + var/datum/queue_link/link = queues[id] + if(isnull(link)) link = new /datum/queue_link(id) queues[id] = link - else - link = queues[id] if(link.add(what, queue_max)) queues -= id +/** + * Pop a queue link without waiting for it to reach its max size. + * This is useful for those links that do not have a fixed size and thus may not pop. + */ +/datum/controller/subsystem/queuelinks/proc/pop_link(id) + if(isnull(id)) + CRASH("Attempted to pop a queue with no ID") + + var/datum/queue_link/link = queues[id] + if(isnull(queues[id])) + CRASH("Attempted to pop a non-existant queue: [id]") + + link.pop() + queues -= id + + /datum/queue_link /// atoms in our queue var/list/partners = list() @@ -50,17 +64,17 @@ SUBSYSTEM_DEF(queuelinks) if(queue_max != 0 && max != 0 && max != queue_max) CRASH("Tried to change queue size to [max] from [queue_max]!") else if(!queue_max) - queue_max = max - + queue_max = max + if(!queue_max || length(partners) < queue_max) return - + pop() return TRUE /datum/queue_link/proc/pop() for(var/atom/item as anything in partners) - item.MatchedLinks(id, partners) + item.MatchedLinks(id, partners - item) qdel(src) /datum/queue_link/Destroy() diff --git a/code/controllers/subsystem/stock_market.dm b/code/controllers/subsystem/stock_market.dm index c9f632c7faf12..6c4341adc8d8a 100644 --- a/code/controllers/subsystem/stock_market.dm +++ b/code/controllers/subsystem/stock_market.dm @@ -1,7 +1,7 @@ SUBSYSTEM_DEF(stock_market) name = "Stock Market" - wait = 20 SECONDS + wait = 60 SECONDS init_order = INIT_ORDER_DEFAULT runlevels = RUNLEVEL_GAME @@ -28,7 +28,7 @@ SUBSYSTEM_DEF(stock_market) materials_trends[possible_market] = rand(MARKET_TREND_DOWNWARD,MARKET_TREND_UPWARD) //aka -1 to 1 materials_trend_life += possible_market - materials_trend_life[possible_market] = rand(1,10) + materials_trend_life[possible_market] = rand(1,3) materials_quantity += possible_market materials_quantity[possible_market] = possible_market.tradable_base_quantity + (rand(-(possible_market.tradable_base_quantity) * 0.5, possible_market.tradable_base_quantity * 0.5)) @@ -80,7 +80,7 @@ SUBSYSTEM_DEF(stock_market) materials_trends[mat] = MARKET_TREND_DOWNWARD else materials_trends[mat] = MARKET_TREND_STABLE - materials_trend_life[mat] = rand(3,10) // Change our trend life for x number of fires of the subsystem + materials_trend_life[mat] = rand(1,3) // Change our trend life for x number of fires of the subsystem else materials_trend_life[mat] -= 1 @@ -88,14 +88,14 @@ SUBSYSTEM_DEF(stock_market) var/quantity_change = 0 switch(trend) if(MARKET_TREND_UPWARD) - price_change = ROUND_UP(gaussian(price_units * 0.1, price_baseline * 0.05)) //If we don't ceil, small numbers will get trapped at low values - quantity_change = -round(gaussian(quantity_baseline * 0.05, quantity_baseline * 0.05)) + price_change = ROUND_UP(gaussian(price_units * 0.30, price_baseline * 0.15)) //If we don't ceil, small numbers will get trapped at low values + quantity_change = -round(gaussian(quantity_baseline * 0.15, quantity_baseline * 0.15)) if(MARKET_TREND_STABLE) price_change = round(gaussian(0, price_baseline * 0.01)) - quantity_change = round(gaussian(0, quantity_baseline * 0.01)) + quantity_change = round(gaussian(0, quantity_baseline * 0.5)) if(MARKET_TREND_DOWNWARD) - price_change = -ROUND_UP(gaussian(price_units * 0.1, price_baseline * 0.05)) - quantity_change = round(gaussian(quantity_baseline * 0.05, quantity_baseline * 0.05)) + price_change = -ROUND_UP(gaussian(price_units * 0.3, price_baseline * 0.15)) + quantity_change = round(gaussian(quantity_baseline * 0.15, quantity_baseline * 0.15)) materials_prices[mat] = round(clamp(price_units + price_change, price_minimum, price_maximum)) materials_quantity[mat] = round(clamp(stock_quantity + quantity_change, 0, quantity_baseline * 2)) 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 a7d43d600b1cd..14f0d03207959 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 @@ -50,7 +50,9 @@ if(living_mob.see_invisible < the_target.invisibility) //Target's invisible to us, forget it return FALSE - if(isturf(living_mob.loc) && 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 + 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 diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 664bf50fd666c..29cf637a86e95 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -45,15 +45,18 @@ /datum/brain_trauma/special/imaginary_friend/proc/make_friend() friend = new(get_turf(owner), owner) -/// Tries an orbit poll for the imaginary friend +/// Tries a poll for the imaginary friend /datum/brain_trauma/special/imaginary_friend/proc/get_ghost() - var/datum/callback/to_call = CALLBACK(src, PROC_REF(add_friend)) - owner.AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_IMAGINARYFRIEND, \ - job_bans = ROLE_PAI, \ - title = "[owner.real_name]'s imaginary friend", \ - to_call = to_call, \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + question = "Do you want to play as [span_danger("[owner.real_name]'s")] [span_notice("imaginary friend")]?", + check_jobban = ROLE_PAI, + poll_time = 20 SECONDS, + checked_target = owner, + ignore_category = POLL_IGNORE_IMAGINARYFRIEND, + alert_pic = owner, + role_name_text = "imaginary friend", ) + add_friend(chosen_one) /// Yay more friends! /datum/brain_trauma/special/imaginary_friend/proc/add_friend(mob/dead/observer/ghost) diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index fc30d7fb3b8f6..d78f2abe9bcf0 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -419,7 +419,7 @@ desc = "Patient seems to oxidise things around them at random, and seem to believe they are aiding a creature in climbing a mountin." scan_desc = "C_)L(#_I_##M;B" gain_text = span_warning("The rusted climb shall finish at the peak") - lose_text = span_notice("The rusted climb? Whats that? An odd dream to be sure.") + lose_text = span_notice("The rusted climb? What's that? An odd dream to be sure.") random_gain = FALSE /datum/brain_trauma/severe/rusting/on_life(seconds_per_tick, times_fired) diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index f6e83c9537c0a..3e2c91efb5da3 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -34,13 +34,16 @@ /// Attempts to get a ghost to play the personality /datum/brain_trauma/severe/split_personality/proc/get_ghost() - var/datum/callback/to_call = CALLBACK(src, PROC_REF(schism)) - owner.AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_SPLITPERSONALITY, \ - job_bans = ROLE_PAI, \ - title = "[owner.real_name]'s [poll_role]", \ - to_call = to_call, \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + question = "Do you want to play as [span_danger("[owner.real_name]'s")] [span_notice(poll_role)]?", + check_jobban = ROLE_PAI, + poll_time = 20 SECONDS, + checked_target = owner, + ignore_category = POLL_IGNORE_SPLITPERSONALITY, + alert_pic = owner, + role_name_text = poll_role, ) + schism(chosen_one) /// Ghost poll has concluded /datum/brain_trauma/severe/split_personality/proc/schism(mob/dead/observer/ghost) @@ -168,7 +171,8 @@ to_chat(src, span_notice("As a split personality, you cannot do anything but observe. However, you will eventually gain control of your body, switching places with the current personality.")) to_chat(src, span_warning("Do not commit suicide or put the body in a deadly position. Behave like you care about it as much as the owner.")) -/mob/living/split_personality/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/split_personality/try_speak(message, ignore_spam, forced, filterproof) + SHOULD_CALL_PARENT(FALSE) to_chat(src, span_warning("You cannot speak, your other self is controlling your body!")) return FALSE @@ -211,10 +215,9 @@ /datum/brain_trauma/severe/split_personality/brainwashing/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [owner.real_name]'s brainwashed mind?", poll_time = 7.5 SECONDS, target_mob = stranger_backseat, pic_source = owner, role_name_text = "brainwashed mind") - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) - stranger_backseat.key = C.key + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger("[owner.real_name]'s")] brainwashed mind?", poll_time = 7.5 SECONDS, checked_target = stranger_backseat, alert_pic = owner, role_name_text = "brainwashed mind") + if(chosen_one) + stranger_backseat.key = chosen_one.key else qdel(src) diff --git a/code/datums/candidate_poll.dm b/code/datums/candidate_poll.dm index 6ccd43c01fded..fc86d70f54690 100644 --- a/code/datums/candidate_poll.dm +++ b/code/datums/candidate_poll.dm @@ -28,6 +28,7 @@ POLL_RESPONSE_TOO_LATE_TO_UNREGISTER = "It's too late to unregister yourself, selection has already begun!", POLL_RESPONSE_UNREGISTERED = "You have been unregistered as a candidate for %ROLE%. You can sign up again before the poll ends.", ) + var/list/chosen_candidates = list() /datum/candidate_poll/New( polled_role, @@ -131,3 +132,14 @@ /datum/candidate_poll/proc/time_left() return duration - (world.time - time_started) + + +/// Print to chat which candidate was selected +/datum/candidate_poll/proc/announce_chosen(list/poll_recipients) + if(!length(chosen_candidates)) + return + for(var/mob/poll_recipient as anything in poll_recipients) + for(var/mob/chosen as anything in chosen_candidates) + if(isnull(chosen)) + continue + to_chat(poll_recipient, span_ooc("[isobserver(poll_recipient) ? FOLLOW_LINK(poll_recipient, chosen) : null][span_warning(" [full_capitalize(role)] Poll: ")][key_name(chosen, include_name = FALSE)] was selected.")) diff --git a/code/datums/components/blob_minion.dm b/code/datums/components/blob_minion.dm index 8261a7ad1fdc3..fb92e1139ccfd 100644 --- a/code/datums/components/blob_minion.dm +++ b/code/datums/components/blob_minion.dm @@ -55,7 +55,7 @@ RegisterSignal(parent, COMSIG_ATOM_FIRE_ACT, PROC_REF(on_burned)) RegisterSignal(parent, COMSIG_ATOM_TRIED_PASS, PROC_REF(on_attempted_pass)) RegisterSignal(parent, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(on_space_move)) - RegisterSignal(parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(parent, COMSIG_MOB_CHANGED_TYPE, PROC_REF(on_transformed)) living_parent.update_appearance(UPDATE_ICON) GLOB.blob_telepathy_mobs |= parent @@ -73,7 +73,7 @@ COMSIG_ATOM_FIRE_ACT, COMSIG_ATOM_TRIED_PASS, COMSIG_ATOM_UPDATE_ICON, - COMSIG_LIVING_TRY_SPEECH, + COMSIG_MOB_TRY_SPEECH, COMSIG_MOB_CHANGED_TYPE, COMSIG_MOB_GET_STATUS_TAB_ITEMS, COMSIG_MOB_MIND_INITIALIZED, diff --git a/code/datums/components/bullet_intercepting.dm b/code/datums/components/bullet_intercepting.dm index c176de54b94c5..32e757c1823e1 100644 --- a/code/datums/components/bullet_intercepting.dm +++ b/code/datums/components/bullet_intercepting.dm @@ -12,8 +12,10 @@ var/mob/wearer /// Callback called when we catch a projectile var/datum/callback/on_intercepted + /// Number of things we can block before we delete ourself (stop being able to block) + var/block_charges = INFINITY -/datum/component/bullet_intercepting/Initialize(block_chance = 2, block_type = BULLET, active_slots, datum/callback/on_intercepted) +/datum/component/bullet_intercepting/Initialize(block_chance = 2, block_type = BULLET, active_slots, datum/callback/on_intercepted, block_charges = INFINITY) . = ..() if (!isitem(parent)) return COMPONENT_INCOMPATIBLE @@ -21,6 +23,7 @@ src.block_type = block_type src.active_slots = active_slots src.on_intercepted = on_intercepted + src.block_charges = block_charges RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_parent_equipped)) RegisterSignal(parent, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(on_unequipped)) @@ -55,11 +58,14 @@ /// Called when wearer is shot, check if we're going to block the hit /datum/component/bullet_intercepting/proc/on_wearer_shot(mob/living/victim, list/signal_args, obj/projectile/bullet) SIGNAL_HANDLER - if (victim != wearer || victim.stat == DEAD || bullet.armor_flag != block_type ) - return + if (victim != wearer || victim.stat == DEAD || bullet.armor_flag != block_type) + return NONE if (!prob(block_chance)) - return + return NONE on_intercepted?.Invoke(victim, bullet) + block_charges-- + if (block_charges <= 0) + qdel(src) return PROJECTILE_INTERRUPT_HIT /// Called when wearer is deleted, stop tracking them diff --git a/code/datums/components/chuunibyou.dm b/code/datums/components/chuunibyou.dm index dda1bdeed5a79..57428bc422358 100644 --- a/code/datums/components/chuunibyou.dm +++ b/code/datums/components/chuunibyou.dm @@ -45,7 +45,7 @@ . = ..() RegisterSignal(parent, COMSIG_MOB_SPELL_PROJECTILE, PROC_REF(on_spell_projectile)) RegisterSignal(parent, COMSIG_MOB_PRE_INVOCATION, PROC_REF(on_pre_invocation)) - RegisterSignal(parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(parent, COMSIG_MOB_AFTER_SPELL_CAST, PROC_REF(on_after_spell_cast)) /datum/component/chuunibyou/UnregisterFromParent() @@ -53,7 +53,7 @@ UnregisterSignal(parent, list( COMSIG_MOB_SPELL_PROJECTILE, COMSIG_MOB_PRE_INVOCATION, - COMSIG_LIVING_TRY_SPEECH, + COMSIG_MOB_TRY_SPEECH, COMSIG_MOB_AFTER_SPELL_CAST, )) @@ -63,7 +63,7 @@ SIGNAL_HANDLER if(casting_spell) - return COMPONENT_CAN_ALWAYS_SPEAK + return COMPONENT_IGNORE_CAN_SPEAK ///signal sent when the parent casts a spell that has a projectile /datum/component/chuunibyou/proc/on_spell_projectile(mob/living/source, datum/action/cooldown/spell/spell, atom/cast_on, obj/projectile/to_fire) diff --git a/code/datums/components/crafting/entertainment.dm b/code/datums/components/crafting/entertainment.dm index d01ffbc00dca5..6de554791a25a 100644 --- a/code/datums/components/crafting/entertainment.dm +++ b/code/datums/components/crafting/entertainment.dm @@ -8,6 +8,16 @@ ) category = CAT_ENTERTAINMENT +/datum/crafting_recipe/sharkplush + name = "Shark Plushie" + result = /obj/item/toy/plush/shark + reqs = list( + /obj/item/clothing/suit/hooded/shark_costume = 1, + /obj/item/grown/cotton = 10, + /obj/item/stack/sheet/cloth = 5, + ) + category = CAT_ENTERTAINMENT + /datum/crafting_recipe/mixedbouquet name = "Mixed bouquet" result = /obj/item/bouquet diff --git a/code/datums/components/crafting/tailoring.dm b/code/datums/components/crafting/tailoring.dm index bb01a4d78dc0f..56a6bfb2c3f18 100644 --- a/code/datums/components/crafting/tailoring.dm +++ b/code/datums/components/crafting/tailoring.dm @@ -345,6 +345,27 @@ ) category = CAT_CLOTHING +/datum/crafting_recipe/shark_costume + name = "shark costume" + result = /obj/item/clothing/suit/hooded/shark_costume + time = 2 SECONDS + reqs = list( + /obj/item/stack/sheet/leather = 5, + /obj/item/stack/sheet/animalhide/carp = 5, + ) + category = CAT_CLOTHING + +/datum/crafting_recipe/shork_costume + name = "shork costume" + result = /obj/item/clothing/suit/hooded/shork_costume + time = 2 SECONDS + tool_behaviors = list(TOOL_WIRECUTTER) + reqs = list( + /obj/item/clothing/suit/hooded/shark_costume = 1, + ) + category = CAT_CLOTHING + + /datum/crafting_recipe/sturdy_shako name = "Sturdy Shako" result = /obj/item/clothing/head/hats/hos/shako diff --git a/code/datums/components/ghost_direct_control.dm b/code/datums/components/ghost_direct_control.dm index a131a2d3ca728..de5bca4fcadde 100644 --- a/code/datums/components/ghost_direct_control.dm +++ b/code/datums/components/ghost_direct_control.dm @@ -16,8 +16,11 @@ /datum/component/ghost_direct_control/Initialize( ban_type = ROLE_SENTIENCE, role_name = null, + poll_question = null, poll_candidates = TRUE, + poll_announce_chosen = TRUE, poll_length = 10 SECONDS, + poll_chat_border_icon = null, poll_ignore_key = POLL_IGNORE_SENTIENCE_POTION, assumed_control_message = null, datum/callback/extra_control_checks, @@ -36,7 +39,7 @@ LAZYADD(GLOB.joinable_mobs[format_text("[initial(mob_parent.name)]")], mob_parent) if (poll_candidates) - INVOKE_ASYNC(src, PROC_REF(request_ghost_control), role_name || "[parent]", poll_length, poll_ignore_key) + INVOKE_ASYNC(src, PROC_REF(request_ghost_control), poll_question, role_name || "[parent]", poll_length, poll_ignore_key, poll_announce_chosen, poll_chat_border_icon) /datum/component/ghost_direct_control/RegisterWithParent() . = ..() @@ -70,23 +73,26 @@ examine_text += span_boldnotice("You could take control of this mob by clicking on it.") /// Send out a request for a brain -/datum/component/ghost_direct_control/proc/request_ghost_control(role_name, poll_length, poll_ignore_key) - if (!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER)) +/datum/component/ghost_direct_control/proc/request_ghost_control(poll_question, role_name, poll_length, poll_ignore_key, poll_announce_chosen, poll_chat_border_icon) + if(!(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER)) return awaiting_ghosts = TRUE - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates( - question = "Do you want to play as [role_name]?", + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + question = poll_question, check_jobban = ban_type, role = ban_type, poll_time = poll_length, + checked_target = parent, ignore_category = poll_ignore_key, - pic_source = parent, + alert_pic = parent, role_name_text = role_name, + chat_text_border_icon = poll_chat_border_icon, + announce_chosen = poll_announce_chosen, ) awaiting_ghosts = FALSE - if (!LAZYLEN(candidates)) + if(isnull(chosen_one)) return - assume_direct_control(pick(candidates)) + assume_direct_control(chosen_one) /// A ghost clicked on us, they want to get in this body /datum/component/ghost_direct_control/proc/on_ghost_clicked(mob/our_mob, mob/dead/observer/hopeful_ghost) diff --git a/code/datums/components/marionette.dm b/code/datums/components/marionette.dm index a2f58031768e0..2777bbb517824 100644 --- a/code/datums/components/marionette.dm +++ b/code/datums/components/marionette.dm @@ -67,6 +67,7 @@ language = language, forced = "[source]'s marionette", saymode = saymode, + message_mods = list(MODE_RELAY = TRUE), ) speech_args[SPEECH_RANGE] = WHISPER_RANGE diff --git a/code/datums/components/material/remote_materials.dm b/code/datums/components/material/remote_materials.dm index e418d4276be10..568b018e58b2b 100644 --- a/code/datums/components/material/remote_materials.dm +++ b/code/datums/components/material/remote_materials.dm @@ -84,23 +84,9 @@ handles linking back and forth. silo = null - var/static/list/allowed_mats = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/silver, - /datum/material/gold, - /datum/material/diamond, - /datum/material/plasma, - /datum/material/uranium, - /datum/material/bananium, - /datum/material/titanium, - /datum/material/bluespace, - /datum/material/plastic, - ) - mat_container = parent.AddComponent( \ /datum/component/material_container, \ - allowed_mats, \ + SSmaterials.materials_by_category[MAT_CATEGORY_SILO], \ local_size, \ mat_container_flags, \ container_signals = mat_container_signals, \ diff --git a/code/datums/components/orbit_poll.dm b/code/datums/components/orbit_poll.dm deleted file mode 100644 index ceb85d16d64c7..0000000000000 --- a/code/datums/components/orbit_poll.dm +++ /dev/null @@ -1,133 +0,0 @@ -/** - * A replacement for the standard poll_ghost_candidate. - * Use this to subtly ask players to join - it picks from orbiters. - * Please use named arguments for this. - * - * @params ignore_key - Required so it doesn't spam - * @params job_bans - You can insert a list or single items here. - * @params cb - Invokes this proc and appends the poll winner as the last argument, mob/dead/observer/ghost - * @params title - Optional. Useful if the role name does not match the parent. - * - * @usage - * ``` - * var/datum/callback/cb = CALLBACK(src, PROC_REF(do_stuff), arg1, arg2) - * AddComponent(/datum/component/orbit_poll, \ - * ignore_key = POLL_IGNORE_EXAMPLE, \ - * job_bans = ROLE_EXAMPLE or list(ROLE_EXAMPLE, ROLE_EXAMPLE2), \ - * title = "Use this if you want something other than the parent name", \ - * to_call = cb, \ - * ) - */ -/datum/component/orbit_poll - /// Prevent players with this ban from being selected - var/list/job_bans = list() - /// Title of the role to announce after it's done - var/title - /// Proc to invoke whenever the poll is complete - var/datum/callback/to_call - -/datum/component/orbit_poll/Initialize( \ - ignore_key, \ - list/job_bans, \ - datum/callback/to_call, \ - title, \ - header = "Ghost Poll", \ - custom_message, \ - timeout = 20 SECONDS \ -) - . = ..() - if (!isatom(parent)) - return COMPONENT_INCOMPATIBLE - - var/atom/owner = parent - - src.job_bans |= job_bans - src.title = title || owner.name - src.to_call = to_call - - var/message = custom_message || "[capitalize(src.title)] is looking for volunteers" - - notify_ghosts( - "[message]. An orbiter will be chosen in [DisplayTimeText(timeout)].\n", - source = parent, - header = "Volunteers requested", - custom_link = " (Ignore)", - ignore_key = ignore_key, - notify_flags = NOTIFY_CATEGORY_NOFLASH, - ) - - addtimer(CALLBACK(src, PROC_REF(end_poll)), timeout, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE|TIMER_DELETE_ME) - -/datum/component/orbit_poll/Topic(href, list/href_list) - if(!href_list["ignore"]) - return - - var/mob/user = usr - - var/ignore_key = href_list["ignore"] - if(tgui_alert(user, "Ignore further [title] alerts?", "Ignore Alert", list("Yes", "No"), 20 SECONDS, TRUE) != "Yes") - return - - GLOB.poll_ignore[ignore_key] |= user.ckey - -/// Concludes the poll, picking one of the orbiters -/datum/component/orbit_poll/proc/end_poll() - if(QDELETED(parent)) - return - - var/list/candidates = list() - var/atom/owner = parent - - var/datum/component/orbiter/orbiter_comp = owner.GetComponent(/datum/component/orbiter) - if(isnull(orbiter_comp)) - phone_home() - return - - for(var/mob/dead/observer/ghost as anything in orbiter_comp.orbiter_list) - var/client/ghost_client = ghost.client - - if(QDELETED(ghost) || isnull(ghost_client)) - continue - - if(is_banned_from(ghost.ckey, job_bans)) - continue - - var/datum/preferences/ghost_prefs = ghost_client.prefs - if(isnull(ghost_prefs)) - candidates += ghost // we'll assume they wanted to be picked despite prefs being null for whatever fucked up reason - continue - - if(!ghost_prefs.read_preference(/datum/preference/toggle/ghost_roles)) - continue - if(!isnull(ghost_client.holder) && !ghost_prefs.read_preference(/datum/preference/toggle/ghost_roles_as_admin)) - continue - - candidates += ghost - - pick_and_offer(candidates) - -/// Takes a list, picks a candidate, and offers the role to them. -/datum/component/orbit_poll/proc/pick_and_offer(list/volunteers) - if(length(volunteers) <= 0) - phone_home() - return - - var/mob/dead/observer/chosen = pick(volunteers) - - if(isnull(chosen)) - phone_home() - return - - SEND_SOUND(chosen, 'sound/misc/notice2.ogg') - var/response = tgui_alert(chosen, "Do you want to assume the role of [title]?", "Orbit Polling", list("Yes", "No"), 10 SECONDS) - if(response != "Yes") - var/reusable_list = volunteers - chosen - return pick_and_offer(reusable_list) - - deadchat_broadcast("[key_name(chosen, include_name = FALSE)] was selected for the role ([title]).", "Ghost Poll: ", parent) - phone_home(chosen) - -/// Make sure to call your parents my dude -/datum/component/orbit_poll/proc/phone_home(mob/dead/observer/chosen) - to_call.Invoke(chosen) - qdel(src) diff --git a/code/datums/components/payment.dm b/code/datums/components/payment.dm index 1220614e9c386..5e79b28fd5724 100644 --- a/code/datums/components/payment.dm +++ b/code/datums/components/payment.dm @@ -19,11 +19,6 @@ var/datum/bank_account/target_acc ///Does this payment component respect same-department-discount? var/department_discount = FALSE - ///A static typecache of all the money-based items that can be actively used as currency. - var/static/list/allowed_money = typecacheof(list( - /obj/item/stack/spacecash, - /obj/item/holochip, - /obj/item/coin)) /datum/component/payment/Initialize(_cost, _target, _style) target_acc = _target @@ -80,13 +75,13 @@ //Here is all the possible non-ID payment methods. var/list/counted_money = list() var/physical_cash_total = 0 - for(var/obj/item/credit in typecache_filter_list(user.get_all_contents(), allowed_money)) //Coins, cash, and credits. + for(var/obj/item/credit in typecache_filter_list(user.get_all_contents(), GLOB.allowed_money)) //Coins, cash, and credits. if(physical_cash_total > total_cost) break physical_cash_total += credit.get_item_credit_value() counted_money += credit - if(is_type_in_typecache(user.pulling, allowed_money) && (physical_cash_total < total_cost)) //Coins(Pulled). + if(is_type_in_typecache(user.pulling, GLOB.allowed_money) && (physical_cash_total < total_cost)) //Coins(Pulled). var/obj/item/counted_credit = user.pulling physical_cash_total += counted_credit.get_item_credit_value() counted_money += counted_credit @@ -134,9 +129,11 @@ * Attempts to charge a mob, user, an integer number of credits, total_cost, directly from an ID card/bank account. */ /datum/component/payment/proc/handle_card(mob/living/user, obj/item/card/id/idcard, total_cost) - var/atom/atom_parent = parent + var/atom/movable/atom_parent = parent if(!idcard) + if(transaction_style == PAYMENT_VENDING) + to_chat(user, span_warning("No card found.")) return FALSE if(!idcard?.registered_account) switch(transaction_style) @@ -146,6 +143,13 @@ to_chat(user, span_warning("ARE YOU JOKING. YOU DON'T HAVE A BANK ACCOUNT ON YOUR ID YOU IDIOT.")) if(PAYMENT_CLINICAL) to_chat(user, span_warning("ID Card lacks a bank account. Advancing.")) + if(PAYMENT_VENDING) + to_chat(user, span_warning("No account found.")) + + return FALSE + + if(!idcard.registered_account.account_job) + atom_parent.say("Departmental accounts have been blacklisted from personal expenses due to embezzlement.") return FALSE if(!(idcard.registered_account.has_money(total_cost))) @@ -156,6 +160,8 @@ to_chat(user, span_warning("YOU MORON. YOU ABSOLUTE BAFOON. YOU INSUFFERABLE TOOL. YOU ARE POOR.")) if(PAYMENT_CLINICAL) to_chat(user, span_warning("ID Card lacks funds. Aborting.")) + if(PAYMENT_VENDING) + to_chat(user, span_warning("You do not possess the funds to purchase that.")) atom_parent.balloon_alert(user, "needs [total_cost] credit\s!") return FALSE target_acc.transfer_money(idcard.registered_account, total_cost, "Nanotrasen: Usage of Corporate Machinery") diff --git a/code/datums/components/sign_language.dm b/code/datums/components/sign_language.dm index 23e40258100c7..e8d22238705eb 100644 --- a/code/datums/components/sign_language.dm +++ b/code/datums/components/sign_language.dm @@ -79,7 +79,7 @@ carbon_parent.verb_yell = "emphatically signs" carbon_parent.bubble_icon = "signlang" RegisterSignal(carbon_parent, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(on_added_organ)) - RegisterSignal(carbon_parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(carbon_parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(carbon_parent, COMSIG_LIVING_TREAT_MESSAGE, PROC_REF(on_treat_living_message)) RegisterSignal(carbon_parent, COMSIG_MOVABLE_USING_RADIO, PROC_REF(on_using_radio)) RegisterSignal(carbon_parent, COMSIG_MOVABLE_SAY_QUOTE, PROC_REF(on_say_quote)) @@ -106,7 +106,7 @@ carbon_parent.bubble_icon = initial(carbon_parent.bubble_icon) UnregisterSignal(carbon_parent, list( COMSIG_CARBON_GAIN_ORGAN, - COMSIG_LIVING_TRY_SPEECH, + COMSIG_MOB_TRY_SPEECH, COMSIG_LIVING_TREAT_MESSAGE, COMSIG_MOVABLE_USING_RADIO, COMSIG_MOVABLE_SAY_QUOTE, @@ -125,7 +125,7 @@ var/obj/item/organ/internal/tongue/new_tongue = new_organ new_tongue.temp_say_mod = "signs" -/// Signal proc for [COMSIG_LIVING_TRY_SPEECH] +/// Signal proc for [COMSIG_MOB_TRY_SPEECH] /// Sign languagers can always speak regardless of they're mute (as long as they're not mimes) /datum/component/sign_language/proc/on_try_speech(mob/living/source, message, ignore_spam, forced) SIGNAL_HANDLER @@ -158,7 +158,7 @@ // Assuming none of the above fail, sign language users can speak // regardless of being muzzled or mute toxin'd or whatever. - return COMPONENT_CAN_ALWAYS_SPEAK + return COMPONENT_IGNORE_CAN_SPEAK /// Checks to see what state this person is in and if they are able to sign or not. /datum/component/sign_language/proc/check_signables_state() diff --git a/code/datums/components/spirit_holding.dm b/code/datums/components/spirit_holding.dm index cb626801d86dc..e2b1cfb96bc3b 100644 --- a/code/datums/components/spirit_holding.dm +++ b/code/datums/components/spirit_holding.dm @@ -37,9 +37,10 @@ ///signal fired on self attacking parent /datum/component/spirit_holding/proc/on_attack_self(datum/source, mob/user) SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(get_ghost), user) +/datum/component/spirit_holding/proc/get_ghost(mob/user) var/atom/thing = parent - if(attempting_awakening) thing.balloon_alert(user, "already channeling!") return @@ -47,20 +48,23 @@ thing.balloon_alert(user, "spirits are unwilling!") to_chat(user, span_warning("Anomalous otherworldly energies block you from awakening [parent]!")) return - attempting_awakening = TRUE thing.balloon_alert(user, "channeling...") - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(affix_spirit), user) - parent.AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_POSSESSED_BLADE, \ - job_bans = ROLE_PAI, \ - to_call = to_call, \ - title = "Spirit of [user.real_name]'s blade", \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + question = "Do you want to play as [span_notice("Spirit of [span_danger("[user.real_name]'s")] blade")]?", + check_jobban = ROLE_PAI, + poll_time = 20 SECONDS, + checked_target = thing, + ignore_category = POLL_IGNORE_POSSESSED_BLADE, + alert_pic = thing, + role_name_text = "possessed blade", + chat_text_border_icon = thing, ) + affix_spirit(user, chosen_one) /// On conclusion of the ghost poll /datum/component/spirit_holding/proc/affix_spirit(mob/awakener, mob/dead/observer/ghost) + var/atom/thing = parent if(isnull(ghost)) diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index a0d0317d75540..b2bae5cffeef8 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -443,7 +443,7 @@ if(human_sacker.get_mob_height() <= HUMAN_HEIGHT_SHORTEST) //JUST YOU WAIT TILL I FIND A CHAIR, BUDDY, THEN YOU'LL BE SORRY attack_mod -= 2 - if(human_sacker.mob_mood.sanity_level == SANITY_INSANE) //I've gone COMPLETELY INSANE + if(human_sacker.mob_mood.sanity_level == SANITY_LEVEL_INSANE) //I've gone COMPLETELY INSANE attack_mod += 15 human_sacker.adjustStaminaLoss(100) //AHAHAHAHAHAHAHAHA diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index 741520ed6ae61..4cf5b4148fed2 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -84,13 +84,12 @@ /datum/disease/transformation/proc/replace_banned_player(mob/living/new_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed. set waitfor = FALSE - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [affected_mob.real_name]?", check_jobban = bantype, role = bantype, poll_time = 5 SECONDS, target_mob = affected_mob, pic_source = affected_mob, role_name_text = "transformation victim") - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_notice(affected_mob.real_name)]?", check_jobban = bantype, role = bantype, poll_time = 5 SECONDS, checked_target = affected_mob, alert_pic = affected_mob, role_name_text = "transformation victim") + if(chosen_one) to_chat(affected_mob, span_userdanger("Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")) - message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") + message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.") affected_mob.ghostize(FALSE) - affected_mob.key = C.key + affected_mob.key = chosen_one.key else to_chat(new_mob, span_userdanger("Your mob has been claimed by death! Appeal your job ban if you want to avoid this in the future!")) new_mob.investigate_log("has been killed because there was no one to replace them as a job-banned player.", INVESTIGATE_DEATHS) diff --git a/code/datums/elements/bed_tucking.dm b/code/datums/elements/bed_tucking.dm index 70b10d4a58c0c..58f5640c31c75 100644 --- a/code/datums/elements/bed_tucking.dm +++ b/code/datums/elements/bed_tucking.dm @@ -1,7 +1,7 @@ /// Tucking element, for things that can be tucked into bed. /datum/element/bed_tuckable element_flags = ELEMENT_BESPOKE - argument_hash_start_idx = 2 + argument_hash_start_idx = 3 /// our pixel_x offset - how much the item moves x when in bed (+x is closer to the pillow) var/x_offset = 0 /// our pixel_y offset - how much the item move y when in bed (-y is closer to the middle) @@ -11,7 +11,7 @@ /// our starting angle for the item var/starting_angle = 0 -/datum/element/bed_tuckable/Attach(obj/target, x = 0, y = 0, rotation = 0) +/datum/element/bed_tuckable/Attach(obj/target, mapload = FALSE, x = 0, y = 0, rotation = 0) . = ..() if(!isitem(target)) return ELEMENT_INCOMPATIBLE @@ -20,6 +20,13 @@ y_offset = y starting_angle = rotation RegisterSignal(target, COMSIG_ITEM_ATTACK_ATOM, PROC_REF(tuck_into_bed)) + if(!mapload) + return + var/turf/our_home = get_turf(target) + var/obj/structure/bed/eepy = locate(/obj/structure/bed) in our_home + if(isnull(eepy)) + return + tuck(target, eepy) /datum/element/bed_tuckable/Detach(obj/target) . = ..() @@ -42,6 +49,10 @@ return to_chat(tucker, span_notice("You lay [tucked] out on [target_bed].")) + tuck(tucked, target_bed) + return COMPONENT_NO_AFTERATTACK + +/datum/element/bed_tuckable/proc/tuck(obj/item/tucked, obj/structure/bed/target_bed) tucked.dir = target_bed.dir tucked.pixel_x = target_bed.dir & EAST ? -x_offset : x_offset tucked.pixel_y = y_offset @@ -50,8 +61,6 @@ tucked.transform = turn(tucked.transform, rotation_degree) RegisterSignal(tucked, COMSIG_ITEM_PICKUP, PROC_REF(untuck)) - return COMPONENT_NO_AFTERATTACK - /** * If we rotate our object, then we need to un-rotate it when it's picked up * diff --git a/code/datums/elements/shatters_when_thrown.dm b/code/datums/elements/can_shatter.dm similarity index 69% rename from code/datums/elements/shatters_when_thrown.dm rename to code/datums/elements/can_shatter.dm index cbb5994852c81..73b025ad83c08 100644 --- a/code/datums/elements/shatters_when_thrown.dm +++ b/code/datums/elements/can_shatter.dm @@ -1,7 +1,8 @@ /** * When attached to something, will make that thing shatter into shards on throw impact or z level falling + * Or even when used as a weapon if the 'shatters_as_weapon' arg is TRUE */ -/datum/element/shatters_when_thrown +/datum/element/can_shatter element_flags = ELEMENT_BESPOKE argument_hash_start_idx = 2 @@ -12,7 +13,12 @@ /// What sound plays when the thing we're attached to shatters var/shattering_sound -/datum/element/shatters_when_thrown/Attach(datum/target, shard_type = /obj/item/plate_shard, number_of_shards = 5, shattering_sound = 'sound/items/ceramic_break.ogg') +/datum/element/can_shatter/Attach(datum/target, + shard_type = /obj/item/plate_shard, + number_of_shards = 5, + shattering_sound = 'sound/items/ceramic_break.ogg', + shatters_as_weapon = FALSE, + ) . = ..() if(!ismovable(target)) @@ -24,26 +30,28 @@ RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(on_throw_impact)) RegisterSignal(target, COMSIG_ATOM_ON_Z_IMPACT, PROC_REF(on_z_impact)) + if(shatters_as_weapon) + RegisterSignal(target, COMSIG_ITEM_POST_ATTACK_ATOM, PROC_REF(on_post_attack_atom)) -/datum/element/shatters_when_thrown/Detach(datum/target) +/datum/element/can_shatter/Detach(datum/target) . = ..() UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT, COMSIG_ATOM_ON_Z_IMPACT)) /// Tells the parent to shatter if we impact a lower zlevel -/datum/element/shatters_when_thrown/proc/on_z_impact(datum/source, turf/impacted_turf, levels) +/datum/element/can_shatter/proc/on_z_impact(datum/source, turf/impacted_turf, levels) SIGNAL_HANDLER shatter(source, impacted_turf) /// Tells the parent to shatter if we are thrown and impact something -/datum/element/shatters_when_thrown/proc/on_throw_impact(datum/source, atom/hit_atom) +/datum/element/can_shatter/proc/on_throw_impact(datum/source, atom/hit_atom) SIGNAL_HANDLER shatter(source, hit_atom) /// Handles the actual shattering part, throwing shards of whatever is defined on the component everywhere -/datum/element/shatters_when_thrown/proc/shatter(atom/movable/source, atom/hit_atom) +/datum/element/can_shatter/proc/shatter(atom/movable/source, atom/hit_atom) var/generator/scatter_gen = generator(GEN_CIRCLE, 0, 48, NORMAL_RAND) var/scatter_turf = get_turf(hit_atom) @@ -64,3 +72,7 @@ return else qdel(source) + +/datum/element/can_shatter/proc/on_post_attack_atom(obj/item/source, atom/attacked_atom, mob/living/user) + SIGNAL_HANDLER + shatter(source, attacked_atom) diff --git a/code/datums/elements/toy_talk.dm b/code/datums/elements/toy_talk.dm new file mode 100644 index 0000000000000..8061eafaeb31b --- /dev/null +++ b/code/datums/elements/toy_talk.dm @@ -0,0 +1,28 @@ +/** + * Allows people to talk via the item with .l or .r + * + * Be sure to override [/atom/movable/proc/GetVoice] if you want the item's "voice" to not default to itself + */ +/datum/element/toy_talk + +/datum/element/toy_talk/Attach(datum/target) + . = ..() + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ITEM_TALK_INTO, PROC_REF(do_talk)) + +/datum/element/toy_talk/Detach(datum/source, ...) + . = ..() + UnregisterSignal(source, COMSIG_ITEM_TALK_INTO) + +/datum/element/toy_talk/proc/do_talk(obj/item/source, mob/speaker, message, channel, list/spans, language, list/message_mods) + SIGNAL_HANDLER + + if(!ismob(speaker) || message_mods[MODE_HEADSET] || message_mods[MODE_RELAY]) + return NONE + + message_mods[MODE_RELAY] = TRUE // Redundant (given NOPASS) but covers our bases + speaker.log_talk(message, LOG_SAY, tag = "toy talk ([source])") + source.say(message, language = language, sanitize = FALSE, message_mods = list(MODE_RELAY = TRUE)) + return NOPASS diff --git a/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm b/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm index 28433ba9064b6..6b9465bf46af0 100644 --- a/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm +++ b/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm @@ -335,3 +335,8 @@ name = "Flower Worn" icon_file = 'icons/mob/clothing/head/hydroponics.dmi' json_config = 'code/datums/greyscale/json_configs/simple_flower_worn.json' + +/datum/greyscale_config/piggy_bank + name = "Piggy Bank" + icon_file = 'icons/obj/fluff/general.dmi' + json_config = 'code/datums/greyscale/json_configs/piggy_bank.json' diff --git a/code/datums/greyscale/json_configs/piggy_bank.json b/code/datums/greyscale/json_configs/piggy_bank.json new file mode 100644 index 0000000000000..71876213e197f --- /dev/null +++ b/code/datums/greyscale/json_configs/piggy_bank.json @@ -0,0 +1,10 @@ +{ + "piggy_bank": [ + { + "type": "icon_state", + "icon_state": "piggy_bank", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ] +} diff --git a/code/datums/lazy_template.dm b/code/datums/lazy_template.dm index 0a5e8e5331413..3b19a17a179da 100644 --- a/code/datums/lazy_template.dm +++ b/code/datums/lazy_template.dm @@ -13,6 +13,8 @@ var/map_dir = "_maps/templates/lazy_templates" /// The filename (without extension) of the map to load var/map_name + /// place_on_top: Whether to use /turf/proc/PlaceOnTop rather than /turf/proc/ChangeTurf + var/place_on_top = FALSE /datum/lazy_template/New() reservations = list() @@ -83,6 +85,7 @@ bottom_left.z, z_upper = z_idx, z_lower = z_idx, + place_on_top = place_on_top, ) for(var/turf/turf as anything in block(bottom_left, top_right)) loaded_turfs += turf diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm index 3c5f6ab62d0cb..e2897a772e530 100644 --- a/code/datums/materials/basemats.dm +++ b/code/datums/materials/basemats.dm @@ -4,7 +4,7 @@ desc = "Common iron ore often found in sedimentary and igneous layers of the crust." color = "#878687" greyscale_colors = "#878687" - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/iron ore_type = /obj/item/stack/ore/iron value_per_unit = 5 / SHEET_MATERIAL_AMOUNT @@ -25,10 +25,10 @@ color = "#88cdf1" greyscale_colors = "#88cdf196" alpha = 150 - categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) integrity_modifier = 0.1 sheet_type = /obj/item/stack/sheet/glass - ore_type = /obj/item/stack/ore/glass + ore_type = /obj/item/stack/ore/glass/basalt shard_type = /obj/item/shard debris_type = /obj/effect/decal/cleanable/glass value_per_unit = 5 / SHEET_MATERIAL_AMOUNT @@ -47,12 +47,12 @@ /datum/material/glass/on_applied_obj(atom/source, amount, material_flags) . = ..() if(!isstack(source)) - source.AddElement(/datum/element/shatters_when_thrown, shard_type, round(amount / SHEET_MATERIAL_AMOUNT), SFX_SHATTER) + source.AddElement(/datum/element/can_shatter, shard_type, round(amount / SHEET_MATERIAL_AMOUNT), SFX_SHATTER) /datum/material/glass/on_removed(atom/source, amount, material_flags) . = ..() - source.RemoveElement(/datum/element/shatters_when_thrown, shard_type) + source.RemoveElement(/datum/element/can_shatter, shard_type) /* Color matrices are like regular colors but unlike with normal colors, you can go over 255 on a channel. @@ -65,7 +65,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Silver" color = list(255/255, 284/255, 302/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) greyscale_colors = "#e3f1f8" - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/mineral/silver ore_type = /obj/item/stack/ore/silver value_per_unit = 50 / SHEET_MATERIAL_AMOUNT @@ -86,7 +86,7 @@ Unless you know what you're doing, only use the first three numbers. They're in color = list(340/255, 240/255, 50/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //gold is shiny, but not as bright as bananium greyscale_colors = "#dbdd4c" strength_modifier = 1.2 - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/mineral/gold ore_type = /obj/item/stack/ore/gold value_per_unit = 125 / SHEET_MATERIAL_AMOUNT @@ -107,7 +107,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Highly pressurized carbon" color = list(48/255, 272/255, 301/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) greyscale_colors = "#71c8f784" - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/mineral/diamond ore_type = /obj/item/stack/ore/diamond alpha = 132 @@ -130,7 +130,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Uranium" color = rgb(48, 237, 26) greyscale_colors = rgb(48, 237, 26) - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/mineral/uranium ore_type = /obj/item/stack/ore/uranium value_per_unit = 100 / SHEET_MATERIAL_AMOUNT @@ -170,7 +170,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Isn't plasma a state of matter? Oh whatever." color = list(298/255, 46/255, 352/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) greyscale_colors = "#c162ec" - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/mineral/plasma ore_type = /obj/item/stack/ore/plasma value_per_unit = 200 / SHEET_MATERIAL_AMOUNT @@ -204,7 +204,7 @@ Unless you know what you're doing, only use the first three numbers. They're in greyscale_colors = "#4e7dffC8" alpha = 200 starlight_color = COLOR_BLUE - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_ITEM_MATERIAL = TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_ITEM_MATERIAL = TRUE) beauty_modifier = 0.5 sheet_type = /obj/item/stack/sheet/bluespace_crystal ore_type = /obj/item/stack/ore/bluespace_crystal @@ -225,7 +225,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Material with hilarious properties" color = list(460/255, 464/255, 0, 0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //obnoxiously bright yellow greyscale_colors = "#ffff00" - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/mineral/bananium ore_type = /obj/item/stack/ore/bananium value_per_unit = 1000 / SHEET_MATERIAL_AMOUNT @@ -256,7 +256,7 @@ Unless you know what you're doing, only use the first three numbers. They're in color = "#b3c0c7" greyscale_colors = "#b3c0c7" strength_modifier = 1.3 - categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) sheet_type = /obj/item/stack/sheet/mineral/titanium ore_type = /obj/item/stack/ore/titanium value_per_unit = 125 / SHEET_MATERIAL_AMOUNT @@ -297,7 +297,7 @@ Unless you know what you're doing, only use the first three numbers. They're in strength_modifier = 0.85 sheet_type = /obj/item/stack/sheet/plastic ore_type = /obj/item/stack/ore/slag //No plastic or coal ore, so we use slag. - categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) + categories = list(MAT_CATEGORY_SILO = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) value_per_unit = 25 / SHEET_MATERIAL_AMOUNT beauty_modifier = -0.01 armor_modifiers = list(MELEE = 1.5, BULLET = 1.1, LASER = 0.3, ENERGY = 0.5, BOMB = 1, BIO = 1, FIRE = 1.1, ACID = 1) diff --git a/code/datums/mood.dm b/code/datums/mood.dm index 150220cfbcfdb..c61ceea0faf6b 100644 --- a/code/datums/mood.dm +++ b/code/datums/mood.dm @@ -92,7 +92,6 @@ set_sanity(sanity + 0.4 * seconds_per_tick, SANITY_NEUTRAL, SANITY_MAXIMUM) if(MOOD_LEVEL_HAPPY4) set_sanity(sanity + 0.6 * seconds_per_tick, SANITY_NEUTRAL, SANITY_MAXIMUM) - handle_nutrition() // 0.416% is 15 successes / 3600 seconds. Calculated with 2 minute // mood runtime, so 50% average uptime across the hour. @@ -112,16 +111,18 @@ last_stat = mob_parent.stat /// Handles mood given by nutrition -/datum/mood/proc/handle_nutrition() - if (HAS_TRAIT(mob_parent, TRAIT_NOHUNGER)) - clear_mood_event(MOOD_CATEGORY_NUTRITION) // if you happen to switch species while hungry youre no longer hungy - return FALSE // no moods for nutrition +/datum/mood/proc/update_nutrition_moodlets() + if(HAS_TRAIT(mob_parent, TRAIT_NOHUNGER)) + clear_mood_event(MOOD_CATEGORY_NUTRITION) + return FALSE + + if(HAS_TRAIT(mob_parent, TRAIT_FAT) && !HAS_TRAIT(mob_parent, TRAIT_VORACIOUS)) + add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/fat) + return TRUE + switch(mob_parent.nutrition) if(NUTRITION_LEVEL_FULL to INFINITY) - if (!HAS_TRAIT(mob_parent, TRAIT_VORACIOUS)) - add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/fat) - else - add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/wellfed) // round and full + add_mood_event(MOOD_CATEGORY_NUTRITION, HAS_TRAIT(mob_parent, TRAIT_VORACIOUS) ? /datum/mood_event/wellfed : /datum/mood_event/too_wellfed) if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL) add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/wellfed) if( NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) @@ -133,6 +134,8 @@ if(0 to NUTRITION_LEVEL_STARVING) add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/starving) + return TRUE + /** * Adds a mood event to the mob * diff --git a/code/datums/mood_events/eldritch_painting_events.dm b/code/datums/mood_events/eldritch_painting_events.dm index 7df89104263ba..df801998c1d98 100644 --- a/code/datums/mood_events/eldritch_painting_events.dm +++ b/code/datums/mood_events/eldritch_painting_events.dm @@ -17,13 +17,13 @@ mood_change = 5 timeout = 3 MINUTES -/datum/mood_event/eldritch_painting/weeping_withdrawl +/datum/mood_event/eldritch_painting/weeping_withdrawal description = "My mind is clear from his influence." mood_change = 1 timeout = 3 MINUTES /datum/mood_event/eldritch_painting/desire_heretic - description = "A part gained, the manus takes and gives. What did it take from me?" + description = "A part gained, the mansus takes and gives. What did it take from me?" mood_change = -2 timeout = 3 MINUTES diff --git a/code/datums/mood_events/needs_events.dm b/code/datums/mood_events/needs_events.dm index dd710554d8d97..72d789da1e062 100644 --- a/code/datums/mood_events/needs_events.dm +++ b/code/datums/mood_events/needs_events.dm @@ -3,6 +3,10 @@ description = "I'm so fat..." //muh fatshaming mood_change = -6 +/datum/mood_event/too_wellfed + description = "I think I've eaten too much." + mood_change = 0 + /datum/mood_event/wellfed description = "I'm stuffed!" mood_change = 8 diff --git a/code/datums/mutations/chameleon.dm b/code/datums/mutations/chameleon.dm index 3de361ba5485e..d5cbc36d20a1f 100644 --- a/code/datums/mutations/chameleon.dm +++ b/code/datums/mutations/chameleon.dm @@ -19,6 +19,12 @@ /datum/mutation/human/chameleon/on_life(seconds_per_tick, times_fired) owner.alpha = max(owner.alpha - (12.5 * (GET_MUTATION_POWER(src)) * seconds_per_tick), 0) +//Upgraded mutation of the base variant, used for changelings. No instability and better power_coeff +/datum/mutation/human/chameleon/changeling + instability = 0 + power_coeff = 2.5 + locked = TRUE + /** * Resets the alpha of the host to the chameleon default if they move. * diff --git a/code/datums/records/manifest.dm b/code/datums/records/manifest.dm index 789b8787fe6e1..d040c5677d0ec 100644 --- a/code/datums/records/manifest.dm +++ b/code/datums/records/manifest.dm @@ -104,7 +104,10 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new) if(!(person.mind?.assigned_role.job_flags & JOB_CREW_MANIFEST)) return - var/assignment = person.mind.assigned_role.title + // Attempt to get assignment from ID, otherwise default to mind. + var/obj/item/card/id/id_card = person.get_idcard(hand_first = FALSE) + var/assignment = id_card?.get_trim_assignment() || person.mind.assigned_role.title + var/mutable_appearance/character_appearance = new(person.appearance) var/person_gender = "Other" if(person.gender == "male") diff --git a/code/datums/ruins/icemoon.dm b/code/datums/ruins/icemoon.dm index 5d777858fb3cc..c79d7229ceecf 100644 --- a/code/datums/ruins/icemoon.dm +++ b/code/datums/ruins/icemoon.dm @@ -13,62 +13,62 @@ // above ground only /datum/map_template/ruin/icemoon/gas - name = "Lizard Gas Station" + name = "Ice-Ruin Lizard Gas Station" id = "lizgasruin" description = "A gas station. It appears to have been recently open and is in mint condition." suffix = "icemoon_surface_gas.dmm" /datum/map_template/ruin/icemoon/lust - name = "Ruin of Lust" + name = "Ice-Ruin Ruin of Lust" id = "lust" description = "Not exactly what you expected." suffix = "icemoon_surface_lust.dmm" /datum/map_template/ruin/icemoon/asteroid - name = "Asteroid Site" + name = "Ice-Ruin Asteroid Site" id = "asteroidsite" description = "Surprised to see us here?" suffix = "icemoon_surface_asteroid.dmm" /datum/map_template/ruin/icemoon/engioutpost - name = "Engineer Outpost" + name = "Ice-Ruin Engineer Outpost" id = "engioutpost" description = "Blown up by an unfortunate accident." suffix = "icemoon_surface_engioutpost.dmm" /datum/map_template/ruin/icemoon/fountain - name = "Fountain Hall" + name = "Ice-Ruin Fountain Hall" id = "ice_fountain" description = "The fountain has a warning on the side. DANGER: May have undeclared side effects that only become obvious when implemented." prefix = "_maps/RandomRuins/AnywhereRuins/" suffix = "fountain_hall.dmm" /datum/map_template/ruin/icemoon/abandoned_homestead - name = "Abandoned Homestead" + name = "Ice-Ruin Abandoned Homestead" id = "abandoned_homestead" description = "This homestead was once host to a happy homesteading family. It's now host to hungry bears." suffix = "icemoon_underground_abandoned_homestead.dmm" /datum/map_template/ruin/icemoon/entemology - name = "Insect Research Station" + name = "Ice-Ruin Insect Research Station" id = "bug_habitat" description = "An independently funded research outpost, long abandoned. Their mission, to boldly go where no insect life would ever live, ever, and look for bugs." suffix = "icemoon_surface_bughabitat.dmm" /datum/map_template/ruin/icemoon/pizza - name = "Moffuchi's Pizzeria" + name = "Ice-Ruin Moffuchi's Pizzeria" id = "pizzeria" description = "Moffuchi's Family Pizzeria chain has a reputation for providing affordable artisanal meals of questionable edibility. This particular pizzeria seems to have been abandoned for some time." suffix = "icemoon_surface_pizza.dmm" /datum/map_template/ruin/icemoon/frozen_phonebooth - name = "Frozen Phonebooth" + name = "Ice-Ruin Frozen Phonebooth" id = "frozen_phonebooth" description = "A venture by nanotrasen to help popularize the use of holopads. This one was sent to a icemoon." suffix = "icemoon_surface_phonebooth.dmm" /datum/map_template/ruin/icemoon/smoking_room - name = "Smoking Room" + name = "Ice-Ruin Smoking Room" id = "smoking_room" description = "Here lies Charles Morlbaro. He died the way he lived." suffix = "icemoon_surface_smoking_room.dmm" @@ -76,7 +76,7 @@ // above and below ground together /datum/map_template/ruin/icemoon/mining_site - name = "Mining Site" + name = "Ice-Ruin Mining Site" id = "miningsite" description = "Ruins of a site where people once mined with primitive tools for ore." suffix = "icemoon_surface_mining_site.dmm" @@ -84,7 +84,7 @@ always_spawn_with = list(/datum/map_template/ruin/icemoon/underground/mining_site_below = PLACE_BELOW) /datum/map_template/ruin/icemoon/underground/mining_site_below - name = "Mining Site Underground" + name = "Ice-Ruin Mining Site Underground" id = "miningsite-underground" description = "Who knew ladders could be so useful?" suffix = "icemoon_underground_mining_site.dmm" @@ -94,60 +94,60 @@ // below ground only /datum/map_template/ruin/icemoon/underground - name = "underground ruin" + name = "Ice-Ruin underground ruin" ruin_type = ZTRAIT_ICE_RUINS_UNDERGROUND default_area = /area/icemoon/underground/unexplored /datum/map_template/ruin/icemoon/underground/abandonedvillage - name = "Abandoned Village" + name = "Ice-Ruin Abandoned Village" id = "abandonedvillage" description = "Who knows what lies within?" suffix = "icemoon_underground_abandoned_village.dmm" /datum/map_template/ruin/icemoon/underground/library - name = "Buried Library" + name = "Ice-Ruin Buried Library" id = "buriedlibrary" description = "A once grand library, now lost to the confines of the Ice Moon." suffix = "icemoon_underground_library.dmm" /datum/map_template/ruin/icemoon/underground/wrath - name = "Ruin of Wrath" + name = "Ice-Ruin Ruin of Wrath" id = "wrath" description = "You'll fight and fight and just keep fighting." suffix = "icemoon_underground_wrath.dmm" /datum/map_template/ruin/icemoon/underground/hermit - name = "Frozen Shack" + name = "Ice-Ruin Frozen Shack" id = "hermitshack" description = "A place of shelter for a lone hermit, scraping by to live another day." suffix = "icemoon_underground_hermit.dmm" /datum/map_template/ruin/icemoon/underground/lavaland - name = "Lavaland Site" + name = "Ice-Ruin Lavaland Site" id = "lavalandsite" description = "I guess we never really left you huh?" suffix = "icemoon_underground_lavaland.dmm" /datum/map_template/ruin/icemoon/underground/puzzle - name = "Ancient Puzzle" + name = "Ice-Ruin Ancient Puzzle" id = "puzzle" description = "Mystery to be solved." suffix = "icemoon_underground_puzzle.dmm" /datum/map_template/ruin/icemoon/underground/bathhouse - name = "Bath House" + name = "Ice-Ruin Bath House" id = "bathhouse" description = "A warm, safe place." suffix = "icemoon_underground_bathhouse.dmm" /datum/map_template/ruin/icemoon/underground/wendigo_cave - name = "Wendigo Cave" + name = "Ice-Ruin Wendigo Cave" id = "wendigocave" description = "Into the jaws of the beast." suffix = "icemoon_underground_wendigo_cave.dmm" /datum/map_template/ruin/icemoon/underground/free_golem - name = "Free Golem Ship" + name = "Ice-Ruin Free Golem Ship" id = "golem-ship" description = "Lumbering humanoids, made out of precious metals, move inside this ship. They frequently leave to mine more minerals, which they somehow turn into more of them. \ Seem very intent on research and individual liberty, and also geology-based naming?" @@ -155,33 +155,33 @@ suffix = "golem_ship.dmm" /datum/map_template/ruin/icemoon/underground/mailroom - name = "Frozen-over Post Office" + name = "Ice-Ruin Frozen-over Post Office" id = "mailroom" description = "This is where all of your paychecks went. Signed, the management." suffix = "icemoon_underground_mailroom.dmm" /datum/map_template/ruin/icemoon/underground/frozen_comms - name = "Frozen Communicatons Outpost" + name = "Ice-Ruin Frozen Communicatons Outpost" id = "frozen_comms" description = "3 Peaks Radio, where the 2000's live forever." suffix = "icemoon_underground_frozen_comms.dmm" //TODO: Bottom-Level ONLY Spawns after Refactoring Related Code /datum/map_template/ruin/icemoon/underground/plasma_facility - name = "Abandoned Plasma Facility" + name = "Ice-Ruin Abandoned Plasma Facility" id = "plasma_facility" description = "Rumors have developed over the many years of Freyja plasma mining. These rumors suggest that the ghosts of dead mistreated excavation staff have returned to \ exact revenge on their (now former) employers. Coorperate reminds all staff that rumors are just that: Old Housewife tales meant to scare misbehaving kids to bed." suffix = "icemoon_underground_abandoned_plasma_facility.dmm" /datum/map_template/ruin/icemoon/underground/hotsprings - name = "Hot Springs" + name = "Ice-Ruin Hot Springs" id = "hotsprings" description = "Just relax and take a dip, nothing will go wrong, I swear!" suffix = "icemoon_underground_hotsprings.dmm" /datum/map_template/ruin/icemoon/underground/vent - name = "Icemoon Ore Vent" + name = "Ice-Ruin Icemoon Ore Vent" id = "ore_vent_i" description = "A vent that spews out ore. Seems to be a natural phenomenon." //Make this a subtype that only spawns medium and large vents. Some smalls will go to the top level. suffix = "icemoon_underground_ore_vent.dmm" @@ -191,7 +191,7 @@ always_place = TRUE /datum/map_template/ruin/icemoon/ruin/vent - name = "Surface Icemoon Ore Vent" + name = "Ice-Ruin Surface Icemoon Ore Vent" id = "ore_vent_i" description = "A vent that spews out ore. Seems to be a natural phenomenon. Smaller than the underground ones." suffix = "icemoon_surface_ore_vent.dmm" diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index 7627d89cd3f8b..9e562ef0c669a 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -10,27 +10,27 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/biodome/beach - name = "Biodome Beach" + name = "Lava-Ruin Biodome Beach" id = "biodome-beach" description = "Seemingly plucked from a tropical destination, this beach is calm and cool, with the salty waves roaring softly in the background. \ Comes with a rustic wooden bar and suicidal bartender." suffix = "lavaland_biodome_beach.dmm" /datum/map_template/ruin/lavaland/biodome/winter - name = "Biodome Winter" + name = "Lava-Ruin Biodome Winter" id = "biodome-winter" description = "For those getaways where you want to get back to nature, but you don't want to leave the fortified military compound where you spend your days. \ Includes a unique(*) laser pistol display case, and the recently introduced I.C.E(tm)." suffix = "lavaland_surface_biodome_winter.dmm" /datum/map_template/ruin/lavaland/biodome/clown - name = "Biodome Clown Planet" + name = "Lava-Ruin Biodome Clown Planet" id = "biodome-clown" description = "WELCOME TO CLOWN PLANET! HONK HONK HONK etc.!" suffix = "lavaland_biodome_clown_planet.dmm" /datum/map_template/ruin/lavaland/cube - name = "The Wishgranter Cube" + name = "Lava-Ruin The Wishgranter Cube" id = "wishgranter-cube" description = "Nothing good can come from this. Learn from their mistakes and turn around." suffix = "lavaland_surface_cube.dmm" @@ -38,7 +38,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/seed_vault - name = "Seed Vault" + name = "Lava-Ruin Seed Vault" id = "seed-vault" description = "The creators of these vaults were a highly advanced and benevolent race, and launched many into the stars, hoping to aid fledgling civilizations. \ However, all the inhabitants seem to do is grow drugs and guns." @@ -47,7 +47,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/ash_walker - name = "Ash Walker Nest" + name = "Lava-Ruin Ash Walker Nest" id = "ash-walker" description = "A race of unbreathing lizards live here, that run faster than a human can, worship a broken dead city, and are capable of reproducing by something involving tentacles? \ Probably best to stay clear." @@ -56,7 +56,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/syndicate_base - name = "Syndicate Lava Base" + name = "Lava-Ruin Syndicate Lava Base" id = "lava-base" description = "A secret base researching illegal bioweapons, it is closely guarded by an elite team of syndicate agents." suffix = "lavaland_surface_syndicate_base1.dmm" @@ -64,7 +64,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/free_golem - name = "Free Golem Ship" + name = "Lava-Ruin Free Golem Ship" id = "golem-ship" description = "Lumbering humanoids, made out of precious metals, move inside this ship. They frequently leave to mine more minerals, which they somehow turn into more of them. \ Seem very intent on research and individual liberty, and also geology-based naming?" @@ -74,7 +74,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/gaia - name = "Patch of Eden" + name = "Lava-Ruin Patch of Eden" id = "gaia" description = "Who would have thought that such a peaceful place could be on such a horrific planet?" cost = 5 @@ -86,32 +86,32 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/sin/envy - name = "Ruin of Envy" + name = "Lava-Ruin Ruin of Envy" id = "envy" description = "When you get what they have, then you'll finally be happy." suffix = "lavaland_surface_envy.dmm" /datum/map_template/ruin/lavaland/sin/gluttony - name = "Ruin of Gluttony" + name = "Lava-Ruin Ruin of Gluttony" id = "gluttony" description = "If you eat enough, then eating will be all that you do." suffix = "lavaland_surface_gluttony.dmm" /datum/map_template/ruin/lavaland/sin/greed - name = "Ruin of Greed" + name = "Lava-Ruin Ruin of Greed" id = "greed" description = "Sure you don't need magical powers, but you WANT them, and \ that's what's important." suffix = "lavaland_surface_greed.dmm" /datum/map_template/ruin/lavaland/sin/pride - name = "Ruin of Pride" + name = "Lava-Ruin Ruin of Pride" id = "pride" description = "Wormhole lifebelts are for LOSERS, whom you are better than." suffix = "lavaland_surface_pride.dmm" /datum/map_template/ruin/lavaland/sin/sloth - name = "Ruin of Sloth" + name = "Lava-Ruin Ruin of Sloth" id = "sloth" description = "..." suffix = "lavaland_surface_sloth.dmm" @@ -119,7 +119,7 @@ cost = 0 /datum/map_template/ruin/lavaland/ratvar - name = "Dead God" + name = "Lava-Ruin Dead God" id = "ratvar" description = "Ratvar's final resting place." suffix = "lavaland_surface_dead_ratvar.dmm" @@ -127,7 +127,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/hierophant - name = "Hierophant's Arena" + name = "Lava-Ruin Hierophant's Arena" id = "hierophant" description = "A strange, square chunk of metal of massive size. Inside awaits only death and many, many squares." suffix = "lavaland_surface_hierophant.dmm" @@ -135,7 +135,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/blood_drunk_miner - name = "Blood-Drunk Miner" + name = "Lava-Ruin Blood-Drunk Miner" id = "blooddrunk" description = "A strange arrangement of stone tiles and an insane, beastly miner contemplating them." suffix = "lavaland_surface_blooddrunk1.dmm" @@ -143,15 +143,15 @@ allow_duplicates = FALSE //will only spawn one variant of the ruin /datum/map_template/ruin/lavaland/blood_drunk_miner/guidance - name = "Blood-Drunk Miner (Guidance)" + name = "Lava-Ruin Blood-Drunk Miner (Guidance)" suffix = "lavaland_surface_blooddrunk2.dmm" /datum/map_template/ruin/lavaland/blood_drunk_miner/hunter - name = "Blood-Drunk Miner (Hunter)" + name = "Lava-Ruin Blood-Drunk Miner (Hunter)" suffix = "lavaland_surface_blooddrunk3.dmm" /datum/map_template/ruin/lavaland/blood_drunk_miner/random - name = "Blood-Drunk Miner (Random)" + name = "Lava-Ruin Blood-Drunk Miner (Random)" suffix = null always_place = TRUE @@ -160,14 +160,14 @@ return ..() /datum/map_template/ruin/lavaland/ufo_crash - name = "UFO Crash" + name = "Lava-Ruin UFO Crash" id = "ufo-crash" description = "Turns out that keeping your abductees unconscious is really important. Who knew?" suffix = "lavaland_surface_ufo_crash.dmm" cost = 5 /datum/map_template/ruin/lavaland/xeno_nest - name = "Xenomorph Nest" + name = "Lava-Ruin Xenomorph Nest" id = "xeno-nest" description = "These xenomorphs got bored of horrifically slaughtering people on space stations, and have settled down on a nice lava-filled hellscape to focus on what's really important in life. \ Quality memes." @@ -175,7 +175,7 @@ cost = 20 /datum/map_template/ruin/lavaland/fountain - name = "Fountain Hall" + name = "Lava-Ruin Fountain Hall" id = "lava_fountain" description = "The fountain has a warning on the side. DANGER: May have undeclared side effects that only become obvious when implemented." prefix = "_maps/RandomRuins/AnywhereRuins/" @@ -183,14 +183,14 @@ cost = 5 /datum/map_template/ruin/lavaland/survivalcapsule - name = "Survival Capsule Ruins" + name = "Lava-Ruin Survival Capsule Ruins" id = "survivalcapsule" description = "What was once sanctuary to the common miner, is now their tomb." suffix = "lavaland_surface_survivalpod.dmm" cost = 5 /datum/map_template/ruin/lavaland/pizza - name = "Ruined Pizza Party" + name = "Lava-Ruin Ruined Pizza Party" id = "pizza" description = "Little Timmy's birthday pizza bash took a turn for the worse when a bluespace anomaly passed by." suffix = "lavaland_surface_pizzaparty.dmm" @@ -198,7 +198,7 @@ cost = 5 /datum/map_template/ruin/lavaland/cultaltar - name = "Summoning Ritual" + name = "Lava-Ruin Summoning Ritual" id = "cultaltar" description = "A place of vile worship, the scrawling of blood in the middle glowing eerily. A demonic laugh echoes throughout the caverns." suffix = "lavaland_surface_cultaltar.dmm" @@ -206,7 +206,7 @@ cost = 10 /datum/map_template/ruin/lavaland/hermit - name = "Makeshift Shelter" + name = "Lava-Ruin Makeshift Shelter" id = "hermitcave" description = "A place of shelter for a lone hermit, scraping by to live another day." suffix = "lavaland_surface_hermit.dmm" @@ -214,7 +214,7 @@ cost = 10 /datum/map_template/ruin/lavaland/miningripley - name = "Ripley" + name = "Lava-Ruin Ripley" id = "ripley" description = "A heavily-damaged mining ripley, property of a very unfortunate miner. You might have to do a bit of work to fix this thing up." suffix = "lavaland_surface_random_ripley.dmm" @@ -222,14 +222,14 @@ cost = 5 /datum/map_template/ruin/lavaland/dark_wizards - name = "Dark Wizard Altar" + name = "Lava-Ruin Dark Wizard Altar" id = "dark_wizards" description = "A ruin with dark wizards. What secret do they guard?" suffix = "lavaland_surface_wizard.dmm" cost = 5 /datum/map_template/ruin/lavaland/strong_stone - name = "Strong Stone" + name = "Lava-Ruin Strong Stone" id = "strong_stone" description = "A stone that seems particularly powerful." suffix = "lavaland_strong_rock.dmm" @@ -237,14 +237,14 @@ cost = 2 /datum/map_template/ruin/lavaland/puzzle - name = "Ancient Puzzle" + name = "Lava-Ruin Ancient Puzzle" id = "puzzle" description = "Mystery to be solved." suffix = "lavaland_surface_puzzle.dmm" cost = 5 /datum/map_template/ruin/lavaland/elite_tumor - name = "Pulsating Tumor" + name = "Lava-Ruin Pulsating Tumor" id = "tumor" description = "A strange tumor which houses a powerful beast..." suffix = "lavaland_surface_elite_tumor.dmm" @@ -253,7 +253,7 @@ allow_duplicates = TRUE /datum/map_template/ruin/lavaland/elephant_graveyard - name = "Elephant Graveyard" + name = "Lava-Ruin Elephant Graveyard" id = "Graveyard" description = "An abandoned graveyard, calling to those unable to continue." suffix = "lavaland_surface_elephant_graveyard.dmm" @@ -261,7 +261,7 @@ cost = 10 /datum/map_template/ruin/lavaland/bileworm_nest - name = "Bileworm Nest" + name = "Lava-Ruin Bileworm Nest" id = "bileworm_nest" description = "A small sanctuary from the harsh wilderness... if you're a bileworm, that is." cost = 5 @@ -269,7 +269,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/lava_phonebooth - name = "Phonebooth" + name = "Lava-Ruin Phonebooth" id = "lava_phonebooth" description = "A venture by nanotrasen to help popularize the use of holopads. This one somehow made its way here." suffix = "lavaland_surface_phonebooth.dmm" @@ -277,7 +277,7 @@ cost = 5 /datum/map_template/ruin/lavaland/battle_site - name = "Battle Site" + name = "Lava-Ruin Battle Site" id = "battle_site" description = "The long past site of a battle between beast and humanoids. The victor is unknown, but the losers are clear." suffix = "lavaland_battle_site.dmm" @@ -285,7 +285,7 @@ cost = 3 /datum/map_template/ruin/lavaland/vent - name = "Ore Vent" + name = "Lava-Ruin Ore Vent" id = "ore_vent" description = "A vent that spews out ore. Seems to be a natural phenomenon." suffix = "lavaland_surface_ore_vent.dmm" @@ -295,7 +295,7 @@ always_place = TRUE /datum/map_template/ruin/lavaland/watcher_grave - name = "Watchers' Grave" + name = "Lava-Ruin Watchers' Grave" id = "watcher-grave" description = "A lonely cave where an orphaned child awaits a new parent." suffix = "lavaland_surface_watcher_grave.dmm" @@ -303,7 +303,7 @@ allow_duplicates = FALSE /datum/map_template/ruin/lavaland/mook_village - name = "Mook Village" + name = "Lava-Ruin Mook Village" id = "mook_village" description = "A village hosting a community of friendly mooks!" suffix = "lavaland_surface_mookvillage.dmm" diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm index 05c4940dae9f0..76ac14cef5c92 100644 --- a/code/datums/ruins/space.dm +++ b/code/datums/ruins/space.dm @@ -10,65 +10,65 @@ /datum/map_template/ruin/space/zoo id = "zoo" suffix = "abandonedzoo.dmm" - name = "Biological Storage Facility" + name = "Space-Ruin Biological Storage Facility" description = "In case society crumbles, we will be able to restore our zoos to working order with the breeding stock kept in these 100% secure and unbreachable storage facilities. \ At no point has anything escaped. That's our story, and we're sticking to it." /datum/map_template/ruin/space/asteroid1 id = "asteroid1" suffix = "asteroid1.dmm" - name = "Asteroid 1" + name = "Space-Ruin Asteroid 1" description = "I-spy with my little eye, something beginning with R." /datum/map_template/ruin/space/asteroid2 id = "asteroid2" suffix = "asteroid2.dmm" - name = "Asteroid 2" + name = "Space-Ruin Asteroid 2" description = "Oh my god, a giant rock!" /datum/map_template/ruin/space/asteroid3 id = "asteroid3" suffix = "asteroid3.dmm" - name = "Asteroid 3" + name = "Space-Ruin Asteroid 3" description = "This asteroid floating in space has no official designation, because the scientist that discovered it deemed it 'super dull'." /datum/map_template/ruin/space/asteroid4 id = "asteroid4" suffix = "asteroid4.dmm" - name = "Asteroid 4" + name = "Space-Ruin Asteroid 4" description = "Nanotrasen Escape Pods have a 100%* success rate, and a 99%* customer satisfaction rate. \ *Please note that these statistics are taken from pods that have successfully docked with a recovery vessel." /datum/map_template/ruin/space/asteroid5 id = "asteroid5" suffix = "asteroid5.dmm" - name = "Asteroid 5" + name = "Space-Ruin Asteroid 5" description = "Oh my god, another giant rock!" /datum/map_template/ruin/space/asteroid6 id = "asteroid6" suffix = "asteroid6.dmm" - name = "Asteroid 6" + name = "Space-Ruin Asteroid 6" description = "This asteroid has brittle bone disease, so it is fortunate asteroids dont have bones." /datum/map_template/ruin/space/deep_storage id = "deep-storage" suffix = "deepstorage.dmm" - name = "Survivalist Bunker" + name = "Space-Ruin Survivalist Bunker" description = "Assume the best, prepare for the worst. Generally, you should do so by digging a three-man heavily fortified bunker into a giant unused asteroid. \ Then make it self sufficient, mask any evidence of construction, hook it covertly into the telecommunications network and hope for the best." /datum/map_template/ruin/space/bigderelict1 id = "bigderelict1" suffix = "bigderelict1.dmm" - name = "Derelict Tradepost" + name = "Space-Ruin Derelict Tradepost" description = "A once-bustling tradestation that handled imports and exports from nearby stations now lays eerily dormant. \ The last received message was a distress call from one of the on-board officers, but we had no success in making contact again." /datum/map_template/ruin/space/derelict_construction id = "derelict_construction" suffix = "derelict_construction.dmm" - name = "Derelict Construction" + name = "Space-Ruin Derelict Construction" description = "Construction supplies are in high demand due to non-trivial damage routinely sustained by most space stations in this sector. \ Space pirates who dont attempt to rob corporate research stations with only 3 collaborators live long enough to sell captured construction \ equipment back to the highest bidder." @@ -76,14 +76,14 @@ /datum/map_template/ruin/space/derelict_sulaco id = "derelict_sulaco" suffix = "derelict_sulaco.dmm" - name = "Derelict Sulaco" + name = "Space-Ruin Derelict Sulaco" description = "Nothing to see here citizen, move along, certainly no xeno outbreaks here. That purple stuff? It's uh... space nectar... but don't eat it! \ It's the bridge of a top secret military ship." /datum/map_template/ruin/space/derelict2 id = "derelict2" suffix = "derelict2.dmm" - name = "Dinner for Two" + name = "Space-Ruin Dinner for Two" description = "Oh this is the night\n\ It's a beautiful night\n\ And we call it bella notte" @@ -91,161 +91,161 @@ /datum/map_template/ruin/space/derelict3 id = "derelict3" suffix = "derelict3.dmm" - name = "Derelict 3" + name = "Space-Ruin Derelict 3" description = "These hulks were once part of a larger structure, where the three great \[REDACTED\] were forged." /datum/map_template/ruin/space/derelict4 id = "derelict4" suffix = "derelict4.dmm" - name = "Derelict 4" + name = "Space-Ruin Derelict 4" description = "CentCom ferries have never crashed, will never crash, there is no current investigation into a crashed ferry, and we will not let Internal Affairs trample over high security \ information in the name of this baseless witchhunt." /datum/map_template/ruin/space/derelict5 id = "derelict5" suffix = "derelict5.dmm" - name = "Derelict 5" + name = "Space-Ruin Derelict 5" description = "The plan is, we put a whole bunch of crates full of treasure in this disused warehouse, launch it into space, and then ignore it. Forever." /datum/map_template/ruin/space/derelict6 id = "derelict6" suffix = "derelict6.dmm" - name = "Derelict 6" + name = "Space-Ruin Derelict 6" description = "The hush-hush of Nanotrasen when it comes to stations seemingly vanishing off the radar is an interesting topic, theories of nuclear destruction float about while Nanotrasen \ flat-out denies said stations ever existing." /datum/map_template/ruin/space/derelict7 id = "derelict7" suffix = "derelict7.dmm" - name = "Derelict 7" + name = "Space-Ruin Derelict 7" description = "The official report says there was a 'huge explosion' which was 'radical' and 'tubular'. Nothing is said about the explosion's cause." /datum/map_template/ruin/space/derelict8 id = "derelict8" suffix = "derelict8.dmm" - name = "Derelict 8" + name = "Space-Ruin Derelict 8" description = "An auxiliary storage bay might be the least respected room on any functional station, but studies show they are the least likely to be hit in an artillery strike." /datum/map_template/ruin/space/derelict9 id = "derelict9" suffix = "derelict9.dmm" - name = "Derelict 9" + name = "Space-Ruin Derelict 9" description = "Someone already found this high-security supply cache already, but were unable to get inside. Perhaps the next visitor will have more luck." /datum/map_template/ruin/space/empty_shell id = "empty-shell" suffix = "emptyshell.dmm" - name = "Empty Shell" + name = "Space-Ruin Empty Shell" description = "Cosy, rural property available for young professional couple. Only twelve parsecs from the nearest hyperspace lane!" /datum/map_template/ruin/space/the_lizards_gas id = "the-lizards-gas" suffix = "thelizardsgas.dmm" - name = "The Lizard's Gas" + name = "Space-Ruin The Lizard's Gas" description = "A refueling station stocked with enough plasma for any space-worthy vessel. Well, maybe if it weren't 50 years ago." /datum/map_template/ruin/space/intact_empty_ship id = "intact-empty-ship" suffix = "intactemptyship.dmm" - name = "Authorship" + name = "Space-Ruin Authorship" description = "Just somewhere quiet, where I can focus on my work with no interruptions." /datum/map_template/ruin/space/caravanambush id = "caravanambush" suffix = "caravanambush.dmm" - name = "Syndicate Ambush" + name = "Space-Ruin Syndicate Ambush" description = "A caravan route used by passing cargo freights has been ambushed by a salvage team manned by the syndicate. \ The caravan managed to send off a distress message before being surrounded, their video feed cutting off as the sound of gunfire and a parrot was heard." /datum/map_template/ruin/space/originalcontent id = "paperwizard" suffix = "originalcontent.dmm" - name = "A Giant Ball of Paper in Space" + name = "Space-Ruin A Giant Ball of Paper in Space" description = "Sightings of a giant wad of paper hurling through the depths of space have been recently reported by multiple outposts near this sector. \ A giant wad of paper, really? Damn prank callers." /datum/map_template/ruin/space/mech_transport id = "mech-transport" suffix = "mechtransport.dmm" - name = "CF Corsair" + name = "Space-Ruin CF Corsair" description = "Well, when is it getting here? I have bills to pay; very well-armed clients who want their shipments as soon as possible! I don't care, just find it!" /datum/map_template/ruin/space/onehalf id = "onehalf" suffix = "onehalf.dmm" - name = "DK Excavator 453" + name = "Space-Ruin DK Excavator 453" description = "Based on the trace elements we've detected on the gutted asteroids, we suspect that a mining ship using a restricted engine is somewhere in the area. \ We'd like to request a patrol vessel to investigate." /datum/map_template/ruin/space/spacehotel id = "spacehotel" suffix = "spacehotel.dmm" - name = "The Twin-Nexus Hotel" + name = "Space-Ruin The Twin-Nexus Hotel" description = "An interstellar hotel, where the weary spaceman can rest their head and relax, assured that the residental staff will not murder them in their sleep. Probably." /datum/map_template/ruin/space/turreted_outpost id = "turreted-outpost" suffix = "turretedoutpost.dmm" - name = "Unnamed Turreted Outpost" + name = "Space-Ruin Unnamed Turreted Outpost" description = "We'd ask them to stop blaring that ruskiepop music, but none of us are brave enough to go near those death turrets they have." /datum/map_template/ruin/space/oldshuttle id = "spaceman-origins" suffix = "shuttlerelic.dmm" - name = "Strange Ship" + name = "Space-Ruin Strange Ship" description = "A ship seemingly lost, drifting along the stars. This thing looks like it belongs in ancient times." /datum/map_template/ruin/space/way_home id = "way-home" suffix = "way_home.dmm" - name = "Salvation" + name = "Space-Ruin Salvation" description = "In the darkest times, we will find our way home." /datum/map_template/ruin/space/djstation id = "djstation" suffix = "dj_station.dmm" - name = "DJ Station" + name = "Space-Ruin DJ Station" description = "Until very recently this pirate radio station was used to harangue local space stations over a variety of perceived \"ethics violations\". \ It seems like someone finally got sick of it, but the equipment still works." /datum/map_template/ruin/space/thederelict id = "thederelict" suffix = "russian_derelict.dmm" - name = "Kosmicheskaya Stantsiya 13" + name = "Space-Ruin Kosmicheskaya Stantsiya 13" description = "The true fate of Kosmicheskaya Stantsiya 13 is an open question to this day. Most corporations deny its existence, for fear of questioning on what became of its crew." /datum/map_template/ruin/space/abandonedteleporter id = "abandonedteleporter" suffix = "abandonedteleporter.dmm" - name = "Abandoned Teleporter" + name = "Space-Ruin Abandoned Teleporter" description = "In space construction the teleporter is often the first system brought online. \ This lonely, half-built teleporter is a sign of a proposed structure that for one reason or another just never got built." /datum/map_template/ruin/space/crashedclownship id = "crashedclownship" suffix = "crashedclownship.dmm" - name = "Crashed Clown Ship" + name = "Space-Ruin Crashed Clown Ship" description = "For centuries the promise of a new clown homeworld has been the siren call for countless clown vessels. \ Alas, the clown's lust for shenanigans means that successful voyages are almost unheard of, with most vessels falling to hilarious consequences almost immediately." /datum/map_template/ruin/space/crashedship id = "crashedship" suffix = "crashedship.dmm" - name = "Crashed Ship" + name = "Space-Ruin Crashed Ship" description = "The SSCV Atrus was chartered to survey over 600 planets in its maiden voyage. \ Hopefully the SSC is content with an indepth analysis of just this asteroid." /datum/map_template/ruin/space/listeningstation id = "listeningstation" suffix = "listeningstation.dmm" - name = "Syndicate Listening Station" + name = "Space-Ruin Syndicate Listening Station" description = "Listening stations form the backbone of the syndicate's information-gathering operations. \ Assignment to these stations is dreaded by most agents, as it entails long and lonely shifts listening to nearby stations chatter incessantly about the most meaningless things." /datum/map_template/ruin/space/old_ai_sat id = "oldAIsat" suffix = "oldAIsat.dmm" - name = "Abandoned Telecommunications Satellite" + name = "Space-Ruin Abandoned Telecommunications Satellite" description = "When the inspector told the employees that they were all fired, and that their jobs \"could be done by trained lizards anyway\", they reacted badly. \ This event and others is the reason why Central always sends an ERT squad with their competent inspectors. Incompetent inspectors are told they can \"do it alone\" because they're \"that pro\". \ Incompetent inspectors believe this." @@ -253,258 +253,258 @@ /datum/map_template/ruin/space/oldteleporter id = "oldteleporter" suffix = "oldteleporter.dmm" - name = "Detached Teleporter" + name = "Space-Ruin Detached Teleporter" description = "The structure of this surprisingly intact teleporter suggests that it was once part of a larger structure, but what remains of said structure, if anything, can only be guessed at." /datum/map_template/ruin/space/vaporwave id = "vaporwave" suffix = "vaporwave.dmm" - name = "Aesthetic Outpost" + name = "Space-Ruin Aesthetic Outpost" description = "Pause and remember-- You are unique.You are special. Every mistake, trial, and hardship has helped to sculpt your real beauty. \ Stop hating yourself and start appreciating and loving yourself!" /datum/map_template/ruin/space/bus id = "bus" suffix = "bus.dmm" - name = "Waylaid Buses" + name = "Space-Ruin Waylaid Buses" description = "There seems to be a pair of buses that pulled over for repairs. What were they doing...? Their shipment sure seems to be filled with a strange mix. \ Anyway, it looks like some people tried to fix it up for a long time but didn't really get anywhere..." /datum/map_template/ruin/space/oldstation id = "oldstation" suffix = "oldstation.dmm" - name = "Ancient Space Station" + name = "Space-Ruin Ancient Space Station" description = "The crew of a space station awaken one hundred years after a crisis. Awaking to a derelict space station on the verge of collapse, and a hostile force of invading \ hivebots. Can the surviving crew overcome the odds and survive and rebuild, or will the cold embrace of the stars become their new home?" /datum/map_template/ruin/space/gondoland id = "gondolaasteroid" suffix = "gondolaasteroid.dmm" - name = "Gondoland" + name = "Space-Ruin Gondoland" description = "Just an ordinary rock- wait, what's that thing?" /datum/map_template/ruin/space/whiteshipruin_box id = "whiteshipruin_box" suffix = "whiteshipruin_box.dmm" - name = "NT Medical Ship" + name = "Space-Ruin NT Medical Ship" description = "An ancient ship, said to be among the first discovered derelicts near Space Station 13 that was still in working order. \ Aged and deprecated by time, this relic of a vessel is now broken beyond repair." /datum/map_template/ruin/space/whiteshipdock id = "whiteshipdock" suffix = "whiteshipdock.dmm" - name = "Whiteship Dock" + name = "Space-Ruin Whiteship Dock" description = "An abandoned but functional vessel parked in deep space, ripe for the taking." /datum/map_template/ruin/space/cat_experiments id = "meow" suffix = "mrow_thats_right.dmm" - name = "Feline-Human Combination Den" + name = "Space-Ruin Feline-Human Combination Den" description = "With heated debates over the legality of the catperson and their status in the workforce, there's always a place for the blackmarket to slip in for some cash. Whether the results \ are morally sound or not is another issue entirely." /datum/map_template/ruin/space/hilbertresearchfacility id = "hilbert_facility" suffix = "hilbertresearchfacility.dmm" - name = "Hilbert Research Facility" + name = "Space-Ruin Hilbert Research Facility" description = "A research facility of great bluespace discoveries. Long since abandoned, willingly or not..." /datum/map_template/ruin/space/clownplanet id = "clownplanet" suffix = "clownplanet.dmm" - name = "Clown Planet" + name = "Space-Ruin Clown Planet" description = "Thought lost in 2552, this minor planet has recently been rediscovered." /datum/map_template/ruin/space/clericden id = "clericden" suffix = "clericden.dmm" - name = "Cleric's Den" + name = "Space-Ruin Cleric's Den" description = "Once part of a larger monastery, this holy order of long dead clerics practiced far less non-violence than they preached. Appears to have been untouched by looters, however. Odd." /datum/map_template/ruin/space/forgottenship id = "forgottenship" suffix = "forgottenship.dmm" - name = "Syndicate Forgotten Ship" + name = "Space-Ruin Syndicate Forgotten Ship" description = "Seemingly abandoned ship went of course right into NT controlled space. It seems that malfunction caused most systems to turn off, except for sleepers." /datum/map_template/ruin/space/old_syndie_infiltrator id = "old_infiltrator" suffix = "old_infiltrator.dmm" - name = "Abandoned Infiltrator" + name = "Space-Ruin Abandoned Infiltrator" description = "Only one in five Gorlex Marauder strike forces return from their regular raids into Nanotrasen space. \ For the other four... well, their ship doesn't just disappear when their target evacuates." /datum/map_template/ruin/space/hellfactory id = "hellfactory" suffix = "hellfactory.dmm" - name = "Heck Brewery" + name = "Space-Ruin Heck Brewery" description = "An abandoned warehouse and brewing facility, which has been recently rediscovered. Reports claim that the security system entered an ultra-hard lockdown, but these reports are inconclusive." /datum/map_template/ruin/space/space_billboard id = "space_billboard" suffix = "space_billboard.dmm" - name = "Space Billboard" + name = "Space-Ruin Space Billboard" description = "Frequently found alongside well-traversed sublight routes, space billboards have fallen out of favour in recent years as advertisers finally realised that people are incapable of reading billboards going by at over 2/3rds the speed of light." /datum/map_template/ruin/space/spinwardsmoothies id = "spinwardsmoothies" suffix = "spinwardsmoothies.dmm" - name = "Spinward Smoothies" + name = "Space-Ruin Spinward Smoothies" description = "A branch of the beloved Spinward Smoothies chain of smoothie bars." /datum/map_template/ruin/space/cyborg_mothership id = "cyborg_mothership" suffix = "cyborg_mothership.dmm" - name = "Cyborg Mothership" + name = "Space-Ruin Cyborg Mothership" description = "An abandoned cyborg mothership that was overtaken by space vines and hivebots. It appears that it hosted an experimental AI focused on mining before it was depowered." /datum/map_template/ruin/space/dangerous_research id = "dangerous_research" suffix = "dangerous_research.dmm" - name = "Alternate Sciences Research Center" + name = "Space-Ruin Alternate Sciences Research Center" description = "When you're messing with the occult, who knows what you're going to get?" /datum/map_template/ruin/space/anomaly_research id = "anomaly_research" suffix = "anomaly_research.dmm" - name = "Anomaly Research" + name = "Space-Ruin Anomaly Research" description = "A secret research lab embedded in arctic rock, belonging to a Dr Anna Molly. What could she have been researching?" /datum/map_template/ruin/space/meateor id = "meateor" suffix = "meateor.dmm" - name = "Meateor" + name = "Space-Ruin Meateor" description = "A big chunk of meat floating in space. How did it get there?" /datum/map_template/ruin/space/the_faceoff id = "the_faceoff" suffix = "the_faceoff.dmm" - name = "The Faceoff" + name = "Space-Ruin The Faceoff" description = "What do you get when a meeting of the enemy corporations get crashed?" /datum/map_template/ruin/space/meatstation id = "meatderelict" suffix = "meatderelict.dmm" - name = "Bioresearch Outpost" + name = "Space-Ruin Bioresearch Outpost" description = "A bioresearch experiment gone wrong." /datum/map_template/ruin/space/ghost_restaurant id = "space_ghost_restaurant.dmm" suffix = "space_ghost_restaurant.dmm" - name = "Space Ghost Restaurant" + name = "Space-Ruin Space Ghost Restaurant" description = "Ever wondered where the restaurant robots come from? On this ruined station, NTgrub interns dressed up robots in clothes, and sent them to stations to cook their meal orders for them." /datum/map_template/ruin/space/atmosasteroidruin id = "atmosasteroidruin" suffix = "atmosasteroidruin.dmm" - name = "Atmos Asteroid" + name = "Space-Ruin Atmos Asteroid" description = "A dead atmos tech in a continuously pressurizing ruin." /datum/map_template/ruin/space/massdriverrouter id = "fasttravel" suffix = "fasttravel.dmm" - name = "Mass driver Router" + name = "Space-Ruin Mass driver Router" description = "An old, still functional router for some long destroyed system." /datum/map_template/ruin/space/prey_pod id = "prey" suffix = "prey_pod.dmm" - name = "Crashed Mimic Escape Pod" + name = "Space-Ruin Crashed Mimic Escape Pod" description = "A pod with a person who has died to a mimic." /datum/map_template/ruin/space/travelers_rest id = "travelers_rest" suffix = "travelers_rest.dmm" - name = "Traveler's Rest" + name = "Space-Ruin Traveler's Rest" description = "An abandoned capsule floating through space. It seems as if somebody was in here not too long ago." /datum/map_template/ruin/space/prison_shuttle id = "prison_shuttle" suffix = "prison_shuttle.dmm" - name = "Crashed Prisoner Shuttle" + name = "Space-Ruin Crashed Prisoner Shuttle" description = "A prisoner transport shuttle that had crashed into a stray asteroid long ago." /datum/map_template/ruin/space/botanical_haven id = "botanical_haven" suffix = "botanical_haven.dmm" - name = "Botanical Haven" + name = "Space-Ruin Botanical Haven" description = "A small sanctuary for plants and botanists, hidden away in a rusted structure." /datum/map_template/ruin/space/pod_crash id = "pod_crash" suffix = "pod_crash.dmm" - name = "Pod Crash" + name = "Space-Ruin Pod Crash" description = "A tragic display of what happens to drivers who pda and pod." /datum/map_template/ruin/space/interdyne id = "interdyne" suffix = "interdyne.dmm" - name = "Interdyne Spinward Research Base" + name = "Space-Ruin Interdyne Spinward Research Base" description = "An Interdyne facility abandoned due to the accidental discovery of Romerol" /datum/map_template/ruin/space/waystation id = "waystation" suffix = "waystation.dmm" - name = "Waystation" + name = "Space-Ruin Waystation" description = "A waytation for a backwater subsector of Spinward gets attacked by the syndicate due to bad luck." /datum/map_template/ruin/space/allamericandiner id = "allamericandiner" suffix = "allamericandiner.dmm" - name = "The All-American Diner" + name = "Space-Ruin The All-American Diner" description = "A mothballed \"Restaurant\" station of the popular \"The All-American Diner\" franchise." /datum/map_template/ruin/space/mimesvclowns id = "mimesvclowns" suffix = "mimesvsclowns.dmm" - name = "Abandoned Mime Outpost" + name = "Space-Ruin Abandoned Mime Outpost" description = "When you fight mimes, you better bring more than slips." /datum/map_template/ruin/space/transit_booth id = "transit_booth" suffix = "transit_booth.dmm" - name = "Transit Booth" + name = "Space-Ruin Transit Booth" description = "Make sure to check out the duty-free store!" /datum/map_template/ruin/space/space_phonebooth id = "Space_phonebooth" suffix = "phonebooth.dmm" - name = "Space Phonebooth" + name = "Space-Ruin Phonebooth" description = "A venture by nanotrasen to help popularize the use of holopads." /datum/map_template/ruin/space/the_outlet id = "the_outlet" suffix = "the_outlet.dmm" - name = "calebs krazy clothing outlet" + name = "Space-Ruin calebs krazy clothing outlet" description = "A decrepit clothing store built into an asteroid. It appears long since abandoned and has fallen into disrepair." /datum/map_template/ruin/space/infested_frigate id = "infested_frigate" suffix = "infested_frigate.dmm" - name = "SYN-C Brutus" + name = "Space-Ruin SYN-C Brutus" description = "This wasn't an outbreak, this was a repelled attack." /datum/map_template/ruin/space/garbagetruck1 id = "garbagetruck1" suffix = "garbagetruck1.dmm" - name = "Decommissioned Garbage Truck NX1" + name = "Space-Ruin Decommissioned Garbage Truck NX1" description = "An NX-760 interstellar transport barge. At the end of their life cycle, they are often filled with trash and launched into unexplored space to become someone else's problem. This one is full of kitchen waste, and rodents." /datum/map_template/ruin/space/garbagetruck2 id = "garbagetruck2" suffix = "garbagetruck2.dmm" - name = "Decommissioned Garbage Truck NX2" + name = "Space-Ruin Decommissioned Garbage Truck NX2" description = "An NX-760 interstellar transport barge. At the end of their life cycle, they are often filled with trash and launched into unexplored space to become someone else's problem. This one is full of medical waste, and a syndicate agent." /datum/map_template/ruin/space/garbagetruck3 id = "garbagetruck3" suffix = "garbagetruck3.dmm" - name = "Decommissioned Garbage Truck NX3" + name = "Space-Ruin Decommissioned Garbage Truck NX3" description = "An NX-760 interstellar transport barge. At the end of their life cycle, they are often filled with trash and launched into unexplored space to become someone else's problem. This one is full of industrial garbage, and a russian drug den." /datum/map_template/ruin/space/garbagetruck4 id = "garbagetruck4" suffix = "garbagetruck4.dmm" - name = "Decommissioned Garbage Truck NX4" + name = "Space-Ruin Decommissioned Garbage Truck NX4" description = "An NX-760 interstellar transport barge. At the end of their life cycle, they are often filled with trash and launched into unexplored space to become someone else's problem. This one is full of commercial trash, and spiders." diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm index 2f433b8e340fb..f5af223672d63 100644 --- a/code/datums/sprite_accessories.dm +++ b/code/datums/sprite_accessories.dm @@ -2249,6 +2249,10 @@ name = "Moffra" icon_state = "moffra" +/datum/sprite_accessory/moth_wings/lightbearer + name = "Lightbearer" + icon_state = "lightbearer" + /datum/sprite_accessory/moth_antennae //Finally splitting the sprite icon = 'icons/mob/human/species/moth/moth_antennae.dmi' color_src = null @@ -2336,6 +2340,10 @@ name = "Moffra" icon_state = "moffra" +/datum/sprite_accessory/moth_antennae/lightbearer + name = "Lightbearer" + icon_state = "lightbearer" + /datum/sprite_accessory/moth_markings // the markings that moths can have. finally something other than the boring tan icon = 'icons/mob/human/species/moth/moth_markings.dmi' color_src = null @@ -2399,3 +2407,7 @@ /datum/sprite_accessory/moth_markings/witchwing name = "Witch Wing" icon_state = "witchwing" + +/datum/sprite_accessory/moth_markings/lightbearer + name = "Lightbearer" + icon_state = "lightbearer" diff --git a/code/datums/stock_market_events.dm b/code/datums/stock_market_events.dm index 81142d2300224..4907bf784f63a 100644 --- a/code/datums/stock_market_events.dm +++ b/code/datums/stock_market_events.dm @@ -83,7 +83,7 @@ /datum/stock_market_event/large_boost name = "Large Boost!" trend_value = MARKET_TREND_UPWARD - trend_duration = 3 + trend_duration = 4 circumstance = list( "has just released a new product that raised the price of ", "discovered a new valuable use for ", @@ -93,14 +93,14 @@ /datum/stock_market_event/large_boost/start_event() . = ..() var/price_units = SSstock_market.materials_prices[mat] - SSstock_market.materials_prices[mat] += round(gaussian(price_units * 0.5, price_units * 0.1)) + SSstock_market.materials_prices[mat] += round(gaussian(price_units, price_units * 0.15)) SSstock_market.materials_prices[mat] = clamp(SSstock_market.materials_prices[mat], price_minimum * mat.value_per_unit, price_maximum * mat.value_per_unit) create_news() /datum/stock_market_event/large_drop name = "Large Drop!" trend_value = MARKET_TREND_DOWNWARD - trend_duration = 5 + trend_duration = 4 circumstance = list( "'s latest product has seen major controversy, and resulted in a price drop for ", "has been hit with a major lawsuit, resulting in a price drop for ", @@ -110,6 +110,42 @@ /datum/stock_market_event/large_drop/start_event() . = ..() var/price_units = SSstock_market.materials_prices[mat] - SSstock_market.materials_prices[mat] -= round(gaussian(price_units * 1.5, price_units * 0.1)) + SSstock_market.materials_prices[mat] -= round(gaussian(price_units * 1.5, price_units * 0.15)) SSstock_market.materials_prices[mat] = clamp(SSstock_market.materials_prices[mat], price_minimum * mat.value_per_unit, price_maximum * mat.value_per_unit) create_news() + +/datum/stock_market_event/hotcakes + name = "Selling like Hotcakes!" + trend_value = MARKET_TREND_UPWARD + trend_duration = 1 + circumstance = list( + "has just released a new product that is dominating the market for ", + "is hitting it big! Dramatically stocking and raising the price of ", + ", in a surprise move, monopolized supply and has raised the price of ", + ) + +/datum/stock_market_event/hotcakes/start_event() + . = ..() + SSstock_market.materials_prices[mat] = round(price_maximum * mat.value_per_unit) + create_news() + +/datum/stock_market_event/lockdown + name = "Lockdown!" + trend_value = MARKET_TREND_DOWNWARD + trend_duration = 2 + circumstance = list( + "is being investigated by the Galactic Trade Commission, resulting in a halt of trade for ", + ", in a stunning move, has been embargoed by TerraGov, resulting in a halt of trade of ", + ) + +/datum/stock_market_event/lockdown/handle() + . = ..() + SSstock_market.materials_quantity[mat] = 0 //Force the material to be unavailable. + +/datum/stock_market_event/lockdown/end_event() + . = ..() + SSstock_market.materials_quantity[mat] = initial(mat.tradable_base_quantity) //Force the material to be available again. + SSstock_market.materials_prices[mat] = initial(mat.value_per_unit) * SHEET_MATERIAL_AMOUNT //Force the price to be reset once the lockdown is over. + create_news() + + diff --git a/code/game/area/areas/away_content.dm b/code/game/area/areas/away_content.dm index 3b86f56f23587..ded38af6201ab 100644 --- a/code/game/area/areas/away_content.dm +++ b/code/game/area/areas/away_content.dm @@ -35,6 +35,10 @@ Unused icons for new areas are "awaycontent1" ~ "awaycontent30" sound_environment = SOUND_ENVIRONMENT_PLAIN ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/ambiodd.ogg','sound/ambience/ambinice.ogg') +/area/awaymission/museum/cafeteria + name = "Nanotrasen Museum Cafeteria" + sound_environment = SOUND_ENVIRONMENT_ROOM + /area/awaymission/errorroom name = "Super Secret Room" static_lighting = FALSE diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index ea2fbb3eb8eda..09e2bd381fdd4 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -10,7 +10,6 @@ . = ..() AddComponent(/datum/component/simple_rotation) register_context() - update_appearance(UPDATE_ICON_STATE) /obj/structure/frame/computer/deconstruct(disassembled = TRUE) if(!(obj_flags & NO_DECONSTRUCTION)) diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index e624e3f33d705..b3c1e055679d0 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -13,6 +13,10 @@ /// The current (de/con)struction state of the frame var/state = FRAME_STATE_EMPTY +/obj/structure/frame/Initialize(mapload) + . = ..() + update_appearance(UPDATE_ICON_STATE) + /obj/structure/frame/examine(user) . = ..() if(circuit) diff --git a/code/game/machinery/machine_frame.dm b/code/game/machinery/machine_frame.dm index a3c074937f0f7..5a6ff12c3d102 100644 --- a/code/game/machinery/machine_frame.dm +++ b/code/game/machinery/machine_frame.dm @@ -12,7 +12,6 @@ /obj/structure/frame/machine/Initialize(mapload) . = ..() register_context() - update_appearance(UPDATE_ICON_STATE) /obj/structure/frame/machine/Destroy() QDEL_LIST(components) @@ -26,8 +25,8 @@ return ..() /obj/structure/frame/machine/try_dissassemble(mob/living/user, obj/item/tool, disassemble_time) - if(anchored) - balloon_alert(user, "must be unsecured first!") + if(anchored && state == FRAME_STATE_EMPTY) //when using a screwdriver on an incomplete frame(missing components) no point checking for this + balloon_alert(user, "must be unanchored first!") return FALSE return ..() @@ -36,14 +35,15 @@ if(isnull(held_item)) return + if(held_item.tool_behaviour == TOOL_WRENCH && !circuit?.needs_anchored) + context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchor" + return CONTEXTUAL_SCREENTIP_SET + switch(state) if(FRAME_STATE_EMPTY) if(istype(held_item, /obj/item/stack/cable_coil)) context[SCREENTIP_CONTEXT_LMB] = "Wire Frame" return CONTEXTUAL_SCREENTIP_SET - else if(held_item.tool_behaviour == TOOL_WRENCH) - context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchor" - return CONTEXTUAL_SCREENTIP_SET else if(held_item.tool_behaviour == TOOL_WELDER) context[SCREENTIP_CONTEXT_LMB] = "Unweld frame" return CONTEXTUAL_SCREENTIP_SET @@ -61,11 +61,6 @@ if(held_item.tool_behaviour == TOOL_CROWBAR) context[SCREENTIP_CONTEXT_LMB] = "Pry out components" return CONTEXTUAL_SCREENTIP_SET - else if(held_item.tool_behaviour == TOOL_WRENCH) - if(!circuit.needs_anchored) - context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchor" - return CONTEXTUAL_SCREENTIP_SET - return NONE else if(held_item.tool_behaviour == TOOL_SCREWDRIVER) var/needs_components = FALSE for(var/component in req_components) @@ -470,5 +465,6 @@ return TRUE /obj/structure/frame/machine/secured + icon_state = "box_1" state = FRAME_STATE_WIRED anchored = TRUE diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index af6b477e90ba8..d9e3787fd9ead 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -248,7 +248,7 @@ Buildable meters return TRUE // no conflicts found - var/obj/machinery/atmospherics/built_machine = new pipe_type(loc, , , p_init_dir) + var/obj/machinery/atmospherics/built_machine = new pipe_type(loc, null, fixed_dir(), p_init_dir) build_pipe(built_machine) built_machine.on_construction(user, pipe_color, piping_layer) transfer_fingerprints_to(built_machine) @@ -356,9 +356,6 @@ Buildable meters return FALSE /obj/item/pipe/proc/build_pipe(obj/machinery/atmospherics/A) - A.setDir(fixed_dir()) - A.set_init_directions(p_init_dir) - if(pipename) A.name = pipename if(A.on) diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 1014393c00836..5d4fd671c9623 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -19,22 +19,9 @@ var/datum/component/material_container/materials /obj/machinery/recycler/Initialize(mapload) - var/list/allowed_materials = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/silver, - /datum/material/plasma, - /datum/material/gold, - /datum/material/diamond, - /datum/material/plastic, - /datum/material/uranium, - /datum/material/bananium, - /datum/material/titanium, - /datum/material/bluespace - ) materials = AddComponent( /datum/component/material_container, \ - allowed_materials, \ + SSmaterials.materials_by_category[MAT_CATEGORY_SILO], \ INFINITY, \ MATCONTAINER_NO_INSERT \ ) diff --git a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm index 7a04aaf2435f5..412cca0495299 100644 --- a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm +++ b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm @@ -178,7 +178,7 @@ candidate_list += GLOB.current_observers_list candidate_list += GLOB.dead_player_list - var/list/candidates = SSpolling.poll_candidates("Would you like to participate in a spooky ghost swarm? (Warning: you will not be able to return to your body!)", check_jobban = ROLE_SENTIENCE, poll_time = 10 SECONDS, group = candidate_list, pic_source = src, role_name_text = "ghost swarm") + var/list/candidates = SSpolling.poll_candidates("Would you like to participate in a spooky ghost swarm? (Warning: you will not be able to return to your body!)", check_jobban = ROLE_SENTIENCE, poll_time = 10 SECONDS, group = candidate_list, alert_pic = src, role_name_text = "ghost swarm") for(var/mob/dead/observer/candidate_ghost as anything in candidates) var/mob/living/basic/ghost/swarm/new_ghost = new(get_turf(src)) ghosts_spawned += new_ghost diff --git a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm index 87221eeec9fa6..a4880fdc26738 100644 --- a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm +++ b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm @@ -39,12 +39,10 @@ var/datum/action/innate/slime/reproduce/repro_action = new repro_action.Grant(pyro) - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a pyroclastic anomaly slime?", check_jobban = ROLE_SENTIENCE, poll_time = 10 SECONDS, target_mob = pyro, ignore_category = POLL_IGNORE_PYROSLIME, pic_source = pyro, role_name_text = "pyroclastic anomaly slime") - if(!LAZYLEN(candidates)) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = ROLE_SENTIENCE, poll_time = 10 SECONDS, checked_target = pyro, ignore_category = POLL_IGNORE_PYROSLIME, alert_pic = pyro, role_name_text = "pyroclastic anomaly slime") + if(isnull(chosen_one)) return - - var/mob/dead/observer/chosen = pick(candidates) - pyro.key = chosen.key + pyro.key = chosen_one.key pyro.mind.special_role = ROLE_PYROCLASTIC_SLIME pyro.mind.add_antag_datum(/datum/antagonist/pyro_slime) pyro.log_message("was made into a slime by pyroclastic anomaly", LOG_GAME) diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index 7fe6c59075f3f..4b23987b97914 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -129,6 +129,7 @@ desc = "Somebody should remove that." gender = NEUTER layer = WALL_OBJ_LAYER + icon = 'icons/effects/web.dmi' icon_state = "cobweb1" resistance_flags = FLAMMABLE beauty = -100 diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm index 377c8470480be..0c050579de45b 100644 --- a/code/game/objects/effects/effects.dm +++ b/code/game/objects/effects/effects.dm @@ -48,6 +48,7 @@ ///The abstract effect ignores even more effects and is often typechecked for atoms that should truly not be fucked with. /obj/effect/abstract + resistance_flags = parent_type::resistance_flags | SHUTTLE_CRUSH_PROOF /obj/effect/abstract/singularity_pull() return diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index a7934c94f31cb..5c4964cca058a 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -36,7 +36,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark) /obj/effect/landmark/start/proc/after_round_start() // We'd like to keep these around for unit tests, so we can check that they exist. -#ifndef UNIT_TESTS +#if !defined(UNIT_TESTS) && !defined(MAP_TEST) if(delete_after_roundstart) qdel(src) #endif diff --git a/code/game/objects/effects/posters/contraband.dm b/code/game/objects/effects/posters/contraband.dm index 2bb2fcce50e46..52528c251b659 100644 --- a/code/game/objects/effects/posters/contraband.dm +++ b/code/game/objects/effects/posters/contraband.dm @@ -625,3 +625,35 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/blood_geometer icon_state = "singletank_bomb" MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/singletank_bomb, 32) + +///a special poster meant to fool people into thinking this is a bombable wall at a glance. +/obj/structure/sign/poster/contraband/fake_bombable + name = "fake bombable poster" + desc = "We do a little trolling." + icon_state = "fake_bombable" + never_random = TRUE + +/obj/structure/sign/poster/contraband/fake_bombable/Initialize(mapload) + . = ..() + var/turf/our_wall = get_turf_pixel(src) + name = our_wall.name + +/obj/structure/sign/poster/contraband/fake_bombable/examine(mob/user) + var/turf/our_wall = get_turf_pixel(src) + . = our_wall.examine(user) + . += span_notice("It seems to be slightly cracked...") + +/obj/structure/sign/poster/contraband/fake_bombable/ex_act(severity, target) + addtimer(CALLBACK(src, PROC_REF(fall_off_wall)), 2.5 SECONDS) + return FALSE + +/obj/structure/sign/poster/contraband/fake_bombable/proc/fall_off_wall() + if(QDELETED(src) || !isturf(loc)) + return + var/turf/our_wall = get_turf_pixel(src) + our_wall.balloon_alert_to_viewers("it was a ruse!") + roll_and_drop(loc) + playsound(loc, 'sound/items/handling/paper_drop.ogg', 50, TRUE) + + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/fake_bombable, 32) diff --git a/code/game/objects/effects/spawners/random/contraband.dm b/code/game/objects/effects/spawners/random/contraband.dm index ca5acbdbe6767..e65a73cfe4ce7 100644 --- a/code/game/objects/effects/spawners/random/contraband.dm +++ b/code/game/objects/effects/spawners/random/contraband.dm @@ -21,22 +21,20 @@ /obj/item/storage/fancy/cigarettes/cigpack_syndicate = 10, /obj/item/storage/fancy/cigarettes/cigpack_shadyjims = 10, /obj/item/storage/box/donkpockets = 10, + /obj/effect/spawner/random/contraband/plus = 10, /obj/item/reagent_containers/pill/maintenance = 5, - /obj/effect/spawner/random/contraband/plus = 5, ) /obj/effect/spawner/random/contraband/plus name = "contraband loot spawner plus" desc = "Where'd ya find this?" loot = list( - /obj/effect/spawner/random/contraband/prison = 40, /obj/item/clothing/under/syndicate = 20, /obj/item/reagent_containers/cup/bottle/thermite = 20, - /obj/item/reagent_containers/pill/maintenance = 10, /obj/item/restraints/legcuffs/beartrap = 10, - /obj/effect/spawner/random/contraband/narcotics = 10, - /obj/item/seeds/kronkus = 5, - /obj/item/seeds/odious_puffball = 5, + /obj/item/food/drug/saturnx = 5, + /obj/item/reagent_containers/cup/blastoff_ampoule = 5, + /obj/item/food/drug/moon_rock = 5, /obj/item/grenade/empgrenade = 5, /obj/effect/spawner/random/contraband/armory = 1, ) diff --git a/code/game/objects/effects/spawners/random/vending.dm b/code/game/objects/effects/spawners/random/vending.dm index 74ece7f24f93d..014f07d2967c4 100644 --- a/code/game/objects/effects/spawners/random/vending.dm +++ b/code/game/objects/effects/spawners/random/vending.dm @@ -18,6 +18,11 @@ loot_type_path = /obj/machinery/vending/snack loot = list() +/obj/effect/spawner/random/vending/snackvend/Initialize(mapload) + if(check_holidays(HOTDOG_DAY)) + loot += /obj/machinery/vending/hotdog + return ..() + /obj/effect/spawner/random/vending/colavend name = "spawn random cola vending machine" desc = "Automagically transforms into a random cola vendor. If you see this while in a shift, please create a bug report." diff --git a/code/game/objects/effects/spiderwebs.dm b/code/game/objects/effects/spiderwebs.dm index 5023f9bd8254e..2d0f1b9b14de2 100644 --- a/code/game/objects/effects/spiderwebs.dm +++ b/code/game/objects/effects/spiderwebs.dm @@ -1,7 +1,8 @@ -//generic procs copied from obj/effect/alien +#define SPIDER_WEB_TINT "web_colour_tint" + /obj/structure/spider name = "web" - icon = 'icons/effects/effects.dmi' + icon = 'icons/effects/web.dmi' desc = "It's stringy and sticky." anchored = TRUE density = FALSE @@ -22,7 +23,7 @@ damage_amount *= 1.25 if(BRUTE) damage_amount *= 0.25 - . = ..() + return ..() /obj/structure/spider/should_atmos_process(datum/gas_mixture/air, exposed_temperature) return exposed_temperature > 350 @@ -31,17 +32,37 @@ take_damage(5, BURN, 0, 0) /obj/structure/spider/stickyweb + plane = FLOOR_PLANE + layer = MID_TURF_LAYER + icon = 'icons/obj/smooth_structures/stickyweb.dmi' + base_icon_state = "stickyweb" + icon_state = "stickyweb-0" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SPIDER_WEB + canSmoothWith = SMOOTH_GROUP_SPIDER_WEB + SMOOTH_GROUP_WALLS ///Whether or not the web is from the genetics power var/genetic = FALSE ///Whether or not the web is a sealed web var/sealed = FALSE - icon_state = "stickyweb1" + ///Do we need to offset this based on a sprite frill? + var/has_frill = TRUE + /// Chance that someone will get stuck when trying to cross this tile + var/stuck_chance = 50 + /// Chance that a bullet will hit this instead of flying through it + var/projectile_stuck_chance = 30 + +/obj/structure/spider/stickyweb/Initialize(mapload) + // Offset on init so that they look nice in the map editor + if (has_frill) + pixel_x = -9 + pixel_y = -9 + return ..() /obj/structure/spider/stickyweb/attack_hand(mob/user, list/modifiers) .= ..() if(.) return - if(!HAS_TRAIT(user,TRAIT_WEB_WEAVER)) + if(!HAS_TRAIT(user, TRAIT_WEB_WEAVER)) return loc.balloon_alert_to_viewers("weaving...") if(!do_after(user, 2 SECONDS)) @@ -51,11 +72,6 @@ var/obj/item/stack/sheet/cloth/woven_cloth = new /obj/item/stack/sheet/cloth user.put_in_hands(woven_cloth) -/obj/structure/spider/stickyweb/Initialize(mapload) - if(!sealed && prob(50)) - icon_state = "stickyweb2" - . = ..() - /obj/structure/spider/stickyweb/CanAllowThrough(atom/movable/mover, border_dir) . = ..() if(genetic) @@ -67,65 +83,125 @@ return TRUE if(mover.pulledby && HAS_TRAIT(mover.pulledby, TRAIT_WEB_SURFER)) return TRUE - if(prob(50)) - loc.balloon_alert(mover, "stuck in web!") + if(prob(stuck_chance)) + stuck_react(mover) return FALSE - else if(isprojectile(mover)) - return prob(30) - -/obj/structure/spider/stickyweb/sealed - name = "sealed web" - desc = "A solid thick wall of web, airtight enough to block air flow." - icon_state = "sealedweb" - sealed = TRUE - can_atmos_pass = ATMOS_PASS_NO + return . + if(isprojectile(mover)) + return prob(projectile_stuck_chance) + return . -/obj/structure/spider/stickyweb/sealed/Initialize(mapload) - . = ..() - air_update_turf(TRUE, TRUE) +/// Show some feedback when you can't pass through something +/obj/structure/spider/stickyweb/proc/stuck_react(atom/movable/stuck_guy) + loc.balloon_alert(stuck_guy, "stuck in web!") + stuck_guy.Shake(duration = 0.1 SECONDS) -/obj/structure/spider/stickyweb/genetic //for the spider genes in genetics +/// Web made by geneticists, needs special handling to allow them to pass through their own webs +/obj/structure/spider/stickyweb/genetic genetic = TRUE + desc = "It's stringy, sticky, and came out of your coworker." + /// Mob with special permission to cross this web var/mob/living/allowed_mob /obj/structure/spider/stickyweb/genetic/Initialize(mapload, allowedmob) - allowed_mob = allowedmob . = ..() + // Tint it purple so that spiders don't get confused about why they can't cross this one + add_filter(SPIDER_WEB_TINT, 10, list("type" = "outline", "color" = "#ffaaf8ff", "size" = 0.1)) + +/obj/structure/spider/stickyweb/genetic/Initialize(mapload, allowedmob) + allowed_mob = allowedmob + return ..() /obj/structure/spider/stickyweb/genetic/CanAllowThrough(atom/movable/mover, border_dir) - . = ..() //this is the normal spider web return aka a spider would make this TRUE + . = ..() if(mover == allowed_mob) return TRUE else if(isliving(mover)) //we change the spider to not be able to go through here if(mover.pulledby == allowed_mob) return TRUE if(prob(50)) - loc.balloon_alert(mover, "stuck in web!") + stuck_react(mover) return FALSE else if(isprojectile(mover)) return prob(30) + return . -/obj/structure/spider/solid - name = "solid web" - icon = 'icons/effects/effects.dmi' - desc = "A solid wall of web, thick enough to block air flow." - icon_state = "solidweb" +/// Web with a 100% chance to intercept movement +/obj/structure/spider/stickyweb/very_sticky + max_integrity = 20 + desc = "Extremely sticky silk, you're not easily getting through there." + stuck_chance = 100 + projectile_stuck_chance = 100 + +/obj/structure/spider/stickyweb/very_sticky/Initialize(mapload) + . = ..() + add_filter(SPIDER_WEB_TINT, 10, list("type" = "outline", "color" = "#ffffaaff", "size" = 0.1)) + +/obj/structure/spider/stickyweb/very_sticky/update_overlays() + . = ..() + var/mutable_appearance/web_overlay = mutable_appearance(icon = 'icons/effects/web.dmi', icon_state = "sticky_overlay", layer = layer + 1) + web_overlay.pixel_x -= pixel_x + web_overlay.pixel_y -= pixel_y + . += web_overlay + + +/// Web 'wall' +/obj/structure/spider/stickyweb/sealed + name = "sealed web" + desc = "A solid wall of web, dense enough to block air flow." + icon = 'icons/obj/smooth_structures/webwall.dmi' + base_icon_state = "webwall" + icon_state = "webwall-0" + smoothing_groups = SMOOTH_GROUP_SPIDER_WEB_WALL + canSmoothWith = SMOOTH_GROUP_SPIDER_WEB_WALL + plane = GAME_PLANE + layer = OBJ_LAYER + sealed = TRUE + has_frill = FALSE can_atmos_pass = ATMOS_PASS_NO + +/obj/structure/spider/stickyweb/sealed/Initialize(mapload) + . = ..() + air_update_turf(TRUE, TRUE) + +/// Walls which reflects lasers +/obj/structure/spider/stickyweb/sealed/reflector + name = "reflective silk screen" + desc = "Hardened webbing treated with special chemicals which cause it to repel projectiles." + icon = 'icons/obj/smooth_structures/webwall_reflector.dmi' + base_icon_state = "webwall_reflector" + icon_state = "webwall_reflector-0" + smoothing_groups = SMOOTH_GROUP_SPIDER_WEB_WALL_MIRROR + canSmoothWith = SMOOTH_GROUP_SPIDER_WEB_WALL_MIRROR + max_integrity = 30 + opacity = TRUE + flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD + receive_ricochet_chance_mod = INFINITY + +/// Opaque and durable web 'wall' +/obj/structure/spider/stickyweb/sealed/tough + name = "hardened web" + desc = "Webbing hardened through a chemical process into a durable barrier." + icon = 'icons/obj/smooth_structures/webwall_dark.dmi' + base_icon_state = "webwall_dark" + icon_state = "webwall_dark-0" + smoothing_groups = SMOOTH_GROUP_SPIDER_WEB_WALL_TOUGH + canSmoothWith = SMOOTH_GROUP_SPIDER_WEB_WALL_TOUGH opacity = TRUE - density = TRUE max_integrity = 90 layer = ABOVE_MOB_LAYER resistance_flags = FIRE_PROOF | FREEZE_PROOF -/obj/structure/spider/solid/Initialize(mapload) - . = ..() - air_update_turf(TRUE, TRUE) - +/// Web 'door', blocks atmos but not movement /obj/structure/spider/passage name = "web passage" - icon = 'icons/effects/effects.dmi' - desc = "A messy connection of webs blocking the other side, but not solid enough to prevent passage." - icon_state = "webpassage" + desc = "An opaque curtain of web which seals in air but doesn't impede passage." + icon = 'icons/obj/smooth_structures/stickyweb_rotated.dmi' + base_icon_state = "stickyweb_rotated" + icon_state = "stickyweb_rotated-0" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SPIDER_WEB_ROOF + canSmoothWith = SMOOTH_GROUP_SPIDER_WEB_ROOF + SMOOTH_GROUP_WALLS can_atmos_pass = ATMOS_PASS_NO opacity = TRUE max_integrity = 60 @@ -135,7 +211,10 @@ /obj/structure/spider/passage/Initialize(mapload) . = ..() + pixel_x = -9 + pixel_y = -9 air_update_turf(TRUE, TRUE) + add_filter(SPIDER_WEB_TINT, 10, list("type" = "outline", "color" = "#ffffffff", "alpha" = 0.8, "size" = 0.1)) /obj/structure/spider/cocoon name = "cocoon" @@ -165,56 +244,31 @@ A.forceMove(T) return ..() -/obj/structure/spider/sticky - name = "sticky web" - icon = 'icons/effects/effects.dmi' - desc = "Extremely soft and sticky silk." - icon_state = "verystickyweb" - max_integrity = 20 - -/obj/structure/spider/sticky/CanAllowThrough(atom/movable/mover, border_dir) - . = ..() - if(HAS_TRAIT(mover, TRAIT_WEB_SURFER)) - return TRUE - if(!isliving(mover)) - return - if(!isnull(mover.pulledby) && HAS_TRAIT(mover.pulledby, TRAIT_WEB_SURFER)) - return TRUE - loc.balloon_alert(mover, "stuck in web!") - return FALSE - +/// Web caltrops /obj/structure/spider/spikes name = "web spikes" - icon = 'icons/effects/effects.dmi' desc = "Silk hardened into small yet deadly spikes." - icon_state = "webspikes1" + plane = FLOOR_PLANE + layer = MID_TURF_LAYER + icon = 'icons/obj/smooth_structures/stickyweb_spikes.dmi' + base_icon_state = "stickyweb_spikes" + icon_state = "stickyweb_spikes-0" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_SPIDER_WEB + canSmoothWith = SMOOTH_GROUP_SPIDER_WEB + SMOOTH_GROUP_WALLS max_integrity = 40 /obj/structure/spider/spikes/Initialize(mapload) . = ..() + pixel_x = -9 + pixel_y = -9 + add_filter(SPIDER_WEB_TINT, 10, list("type" = "outline", "color" = "#ac0000ff", "size" = 0.1)) AddComponent(/datum/component/caltrop, min_damage = 20, max_damage = 30, flags = CALTROP_NOSTUN | CALTROP_BYPASS_SHOES) -/obj/structure/spider/reflector - name = "Reflective silk screen" - icon = 'icons/effects/effects.dmi' - desc = "Made up of an extremly reflective silk material looking at it hurts." - icon_state = "reflector" - max_integrity = 30 - density = TRUE - opacity = TRUE - anchored = TRUE - flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD - receive_ricochet_chance_mod = INFINITY - -/obj/structure/spider/reflector/Initialize(mapload) - . = ..() - air_update_turf(TRUE, TRUE) - /obj/structure/spider/effigy name = "web effigy" - icon = 'icons/effects/effects.dmi' desc = "A giant spider! Fortunately, this one is just a statue of hardened webbing." - icon_state = "webcarcass" + icon_state = "effigy" max_integrity = 125 density = TRUE anchored = FALSE @@ -222,3 +276,5 @@ /obj/structure/spider/effigy/Initialize(mapload) . = ..() AddElement(/datum/element/temporary_atom, 1 MINUTES) + +#undef SPIDER_WEB_TINT diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 62d128b4eb560..d0dc7e54560ed 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -606,8 +606,23 @@ playsound(src, block_sound, BLOCK_SOUND_VOLUME, vary = TRUE) return TRUE -/obj/item/proc/talk_into(mob/M, input, channel, spans, datum/language/language, list/message_mods) - return ITALICS | REDUCE_RANGE +/** + * Handles someone talking INTO an item + * + * Commonly used by someone holding it and using .r or .l + * Also used by radios + * + * * speaker - the atom that is doing the talking + * * message - the message being spoken + * * channel - the channel the message is being spoken on, only really used for radios + * * spans - the spans of the message + * * language - the language the message is in + * * message_mods - any message mods that should be applied to the message + * + * Return a flag that modifies the original message + */ +/obj/item/proc/talk_into(atom/movable/speaker, message, channel, list/spans, datum/language/language, list/message_mods) + return SEND_SIGNAL(src, COMSIG_ITEM_TALK_INTO, speaker, message, channel, spans, language, message_mods) || (ITALICS|REDUCE_RANGE) /// Called when a mob drops an item. /obj/item/proc/dropped(mob/user, silent = FALSE) diff --git a/code/game/objects/items/busts_and_figurines.dm b/code/game/objects/items/busts_and_figurines.dm new file mode 100644 index 0000000000000..afc4a58334e90 --- /dev/null +++ b/code/game/objects/items/busts_and_figurines.dm @@ -0,0 +1,139 @@ +/obj/item/statuebust + name = "bust" + desc = "A priceless ancient marble bust, the kind that belongs in a museum." //or you can hit people with it + icon = 'icons/obj/art/statue.dmi' + icon_state = "bust" + force = 15 + throwforce = 10 + throw_speed = 5 + throw_range = 2 + attack_verb_continuous = list("busts") + attack_verb_simple = list("bust") + var/impressiveness = 45 + +/obj/item/statuebust/Initialize(mapload) + . = ..() + AddElement(/datum/element/art, impressiveness) + AddElement(/datum/element/beauty, 1000) + +/obj/item/statuebust/hippocratic + name = "hippocrates bust" + desc = "A bust of the famous Greek physician Hippocrates of Kos, often referred to as the father of western medicine." + icon_state = "hippocratic" + impressiveness = 50 + // If it hits the prob(reference_chance) chance, this is set to TRUE. Adds medical HUD when wielded, but has a 10% slower attack speed and is too bloody to make an oath with. + var/reference = FALSE + // Chance for above. + var/reference_chance = 1 + // Minimum time inbetween oaths. + COOLDOWN_DECLARE(oath_cd) + +/obj/item/statuebust/hippocratic/evil + reference_chance = 100 + +/obj/item/statuebust/hippocratic/Initialize(mapload) + . = ..() + if(prob(reference_chance)) + name = "Solemn Vow" + desc = "Art lovers will cherish the bust of Hippocrates, commemorating a time when medics still thought doing no harm was a good idea." + attack_speed = CLICK_CD_SLOW + reference = TRUE + +/obj/item/statuebust/hippocratic/examine(mob/user) + . = ..() + if(reference) + . += span_notice("You could activate the bust in-hand to swear or forswear a Hippocratic Oath... but it seems like somebody decided it was more of a Hippocratic Suggestion. This thing is caked with bits of blood and gore.") + return + . += span_notice("You can activate the bust in-hand to swear or forswear a Hippocratic Oath! This has no effects except pacifism or bragging rights. Does not remove other sources of pacifism. Do not eat.") + +/obj/item/statuebust/hippocratic/equipped(mob/living/carbon/human/user, slot) + ..() + if(!(slot & ITEM_SLOT_HANDS)) + return + var/datum/atom_hud/our_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + our_hud.show_to(user) + ADD_TRAIT(user, TRAIT_MEDICAL_HUD, type) + +/obj/item/statuebust/hippocratic/dropped(mob/living/carbon/human/user) + ..() + if(HAS_TRAIT_NOT_FROM(user, TRAIT_MEDICAL_HUD, type)) + return + var/datum/atom_hud/our_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + our_hud.hide_from(user) + REMOVE_TRAIT(user, TRAIT_MEDICAL_HUD, type) + +/obj/item/statuebust/hippocratic/attack_self(mob/user) + if(!iscarbon(user)) + to_chat(user, span_warning("You remember how the Hippocratic Oath specifies 'my fellow human beings' and realize that it's completely meaningless to you.")) + return + + if(reference) + to_chat(user, span_warning("As you prepare yourself to swear the Oath, you realize that doing so on a blood-caked bust is probably not a good idea.")) + return + + if(!COOLDOWN_FINISHED(src, oath_cd)) + to_chat(user, span_warning("You've sworn or forsworn an oath too recently to undo your decisions. The bust looks at you with disgust.")) + return + + COOLDOWN_START(src, oath_cd, 5 MINUTES) + + if(HAS_TRAIT_FROM(user, TRAIT_PACIFISM, type)) + to_chat(user, span_warning("You've already sworn a vow. You start preparing to rescind it...")) + if(do_after(user, 5 SECONDS, target = user)) + user.say("Yeah this Hippopotamus thing isn't working out. I quit!", forced = "hippocratic hippocrisy") + REMOVE_TRAIT(user, TRAIT_PACIFISM, type) + + // they can still do it for rp purposes + if(HAS_TRAIT_NOT_FROM(user, TRAIT_PACIFISM, type)) + to_chat(user, span_warning("You already don't want to harm people, this isn't going to do anything!")) + + + to_chat(user, span_notice("You remind yourself of the Hippocratic Oath's contents and prepare to swear yourself to it...")) + if(do_after(user, 4 SECONDS, target = user)) + user.say("I swear to fulfill, to the best of my ability and judgment, this covenant:", forced = "hippocratic oath") + else + return fuck_it_up(user) + if(do_after(user, 2 SECONDS, target = user)) + user.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.", forced = "hippocratic oath") + else + return fuck_it_up(user) + if(do_after(user, 3 SECONDS, target = user)) + user.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.", forced = "hippocratic oath") + else + + return fuck_it_up(user) + if(do_after(user, 3 SECONDS, target = user)) + user.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.", forced = "hippocratic oath") + else + return fuck_it_up(user) + + to_chat(user, span_notice("Contentment, understanding, and purpose washes over you as you finish the oath. You consider for a second the concept of harm and shudder.")) + ADD_TRAIT(user, TRAIT_PACIFISM, type) + +// Bully the guy for fucking up. +/obj/item/statuebust/hippocratic/proc/fuck_it_up(mob/living/carbon/user) + to_chat(user, span_warning("You forget what comes next like a dumbass. The Hippocrates bust looks down on you, disappointed.")) + user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2) + COOLDOWN_RESET(src, oath_cd) + +/obj/item/maneki_neko + name = "Maneki-Neko" + desc = "A figurine of a cat holding a coin, said to bring fortune and wealth, and perpetually moving its paw in a beckoning gesture." + icon = 'icons/obj/fluff/general.dmi' + icon_state = "maneki-neko" + w_class = WEIGHT_CLASS_SMALL + force = 5 + throwforce = 5 + throw_speed = 3 + throw_range = 5 + attack_verb_continuous = list("bashes", "beckons", "hit") + attack_verb_simple = list("bash", "beckon", "hit") + +/obj/item/maneki_neko/Initialize(mapload) + . = ..() + //Not compatible with greyscale configs because it's animated. + color = pick_weight(list(COLOR_WHITE = 3, COLOR_GOLD = 2, COLOR_DARK = 1)) + var/mutable_appearance/neko_overlay = mutable_appearance(icon, "maneki-neko-overlay", appearance_flags = RESET_COLOR) + add_overlay(neko_overlay) + AddElement(/datum/element/art, GOOD_ART) + AddElement(/datum/element/beauty, 800) diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 26363b57bde6a..bde08149b4ef3 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -445,7 +445,7 @@ context[SCREENTIP_CONTEXT_RMB] = "Project pay stand" if(isnull(registered_account) || registered_account.replaceable) //Same check we use when we check if we can assign an account context[SCREENTIP_CONTEXT_ALT_RMB] = "Assign account" - if(!registered_account.replaceable || registered_account.account_balance > 0) + else if(registered_account.account_balance > 0) context[SCREENTIP_CONTEXT_ALT_LMB] = "Withdraw credits" return CONTEXTUAL_SCREENTIP_SET diff --git a/code/game/objects/items/devices/aicard_evil.dm b/code/game/objects/items/devices/aicard_evil.dm index f91150bb086a6..8aaa9f0311116 100644 --- a/code/game/objects/items/devices/aicard_evil.dm +++ b/code/game/objects/items/devices/aicard_evil.dm @@ -34,14 +34,16 @@ if(isnull(op_datum)) balloon_alert(user, "invalid access!") return - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user, op_datum) - AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_SYNDICATE, \ - job_bans = ROLE_OPERATIVE, \ - to_call = to_call, \ - title = "Nuclear Operative Modsuit AI" \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + check_jobban = ROLE_OPERATIVE, + poll_time = 20 SECONDS, + checked_target = src, + ignore_category = POLL_IGNORE_SYNDICATE, + alert_pic = src, + role_name_text = "Nuclear Operative Modsuit AI", + chat_text_border_icon = mutable_appearance(icon, "syndicard-full"), ) + on_poll_concluded(user, op_datum, chosen_one) /// Poll has concluded with a ghost, create the AI /obj/item/aicard/syndie/loaded/proc/on_poll_concluded(mob/user, datum/antagonist/nukeop/op_datum, mob/dead/observer/ghost) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 248955e0fa493..433cf51062f3e 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -262,17 +262,29 @@ /obj/item/radio/talk_into(atom/movable/talking_movable, message, channel, list/spans, datum/language/language, list/message_mods) if(SEND_SIGNAL(talking_movable, COMSIG_MOVABLE_USING_RADIO, src) & COMPONENT_CANNOT_USE_RADIO) - return + return NONE if(SEND_SIGNAL(src, COMSIG_RADIO_NEW_MESSAGE, talking_movable, message, channel) & COMPONENT_CANNOT_USE_RADIO) - return + return NONE if(!spans) spans = list(talking_movable.speech_span) if(!language) language = talking_movable.get_selected_language() - INVOKE_ASYNC(src, PROC_REF(talk_into_impl), talking_movable, message, channel, spans.Copy(), language, message_mods) + INVOKE_ASYNC(src, PROC_REF(talk_into_impl), talking_movable, message, channel, LAZYLISTDUPLICATE(spans), language, LAZYLISTDUPLICATE(message_mods)) return ITALICS | REDUCE_RANGE +/** + * Handles talking into the radio + * + * Unlike most speech related procs, spans and message_mods are not guaranteed to be lists + * + * * talking_movable - the atom that is talking + * * message - the message to be spoken + * * channel - the channel to be spoken on + * * spans - the spans to be used, lazylist + * * language - the language to be spoken in. (Should) never be null + * * message_mods - the message mods to be used, lazylist + */ /obj/item/radio/proc/talk_into_impl(atom/movable/talking_movable, message, channel, list/spans, datum/language/language, list/message_mods) if(!on) return // the device has to be on @@ -358,9 +370,9 @@ /obj/item/radio/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) . = ..() - if(radio_freq || !broadcasting || get_dist(src, speaker) > canhear_range) + if(radio_freq || !broadcasting || get_dist(src, speaker) > canhear_range || message_mods[MODE_RELAY]) return - var/filtered_mods = list() + var/list/filtered_mods = list() if (message_mods[MODE_SING]) filtered_mods[MODE_SING] = message_mods[MODE_SING] diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index ef922ed3a7d3c..83ea668a9fb28 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -33,7 +33,8 @@ /obj/item/healthanalyzer/examine(mob/user) . = ..() - . += span_notice("Alt-click [src] to toggle the limb damage readout.") + if(src.mode != SCANNER_NO_MODE) + . += span_notice("Alt-click [src] to toggle the limb damage readout.") /obj/item/healthanalyzer/suicide_act(mob/living/carbon/user) user.visible_message(span_suicide("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!")) diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 985d2761a0271..c686eeb04a7a7 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -159,6 +159,9 @@ /obj/item/taperecorder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, list/message_mods = list(), message_range) . = ..() + if(message_mods[MODE_RELAY]) + return + if(mytape && recording) mytape.timestamp += mytape.used_capacity mytape.storedinfo += "\[[time2text(mytape.used_capacity,"mm:ss")]\] [raw_message]" diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm index 6f425b7df6eab..ca2a972006da6 100644 --- a/code/game/objects/items/dice.dm +++ b/code/game/objects/items/dice.dm @@ -425,11 +425,10 @@ var/mob/living/carbon/human/human_servant = new(drop_location()) do_smoke(0, holder = src, location = drop_location()) - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [user.real_name]'s Servant?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 5 SECONDS, target_mob = human_servant, pic_source = user, role_name_text = "dice servant") - if(LAZYLEN(candidates)) - var/mob/dead/observer/candidate = pick(candidates) - message_admins("[ADMIN_LOOKUPFLW(candidate)] was spawned as Dice Servant") - human_servant.key = candidate.key + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger("[user.real_name]'s")] [span_notice("Servant")]?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 5 SECONDS, checked_target = human_servant, alert_pic = user, role_name_text = "dice servant") + if(chosen_one) + message_admins("[ADMIN_LOOKUPFLW(chosen_one)] was spawned as Dice Servant") + human_servant.key = chosen_one.key human_servant.equipOutfit(/datum/outfit/butler) var/datum/mind/servant_mind = new /datum/mind() diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index 920d074ad4a4e..b0189a0c4ecef 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -279,13 +279,8 @@ /obj/item/extinguisher/proc/EmptyExtinguisher(mob/user) if(loc == user && reagents.total_volume) + reagents.expose(user.loc, TOUCH) reagents.clear_reagents() - - var/turf/T = get_turf(loc) - if(isopenturf(T)) - var/turf/open/theturf = T - theturf.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS) - user.visible_message(span_notice("[user] empties out \the [src] onto the floor using the release valve."), span_info("You quietly empty out \the [src] using its release valve.")) //firebot assembly @@ -297,3 +292,10 @@ user.put_in_hands(new /obj/item/bot_assembly/firebot) else ..() + +/obj/item/extinguisher/anti + name = "fire extender" + desc = "A traditional red fire extinguisher. Made in Britain... wait, what?" + chem = /datum/reagent/fuel + tanktype = /obj/structure/reagent_dispensers/fueltank + cooling_power = 0 diff --git a/code/game/objects/items/food/bread.dm b/code/game/objects/items/food/bread.dm index ba1845bb40da4..0f95aac6d8528 100644 --- a/code/game/objects/items/food/bread.dm +++ b/code/game/objects/items/food/bread.dm @@ -481,6 +481,7 @@ foodtypes = GRAIN | DAIRY w_class = WEIGHT_CLASS_SMALL crafting_complexity = FOOD_COMPLEXITY_2 + custom_price = PAYCHECK_CREW /obj/item/food/butterdog/Initialize(mapload) . = ..() diff --git a/code/game/objects/items/food/lizard.dm b/code/game/objects/items/food/lizard.dm index 729ad4d38a971..47b5ff7510916 100644 --- a/code/game/objects/items/food/lizard.dm +++ b/code/game/objects/items/food/lizard.dm @@ -34,6 +34,7 @@ foodtypes = MEAT w_class = WEIGHT_CLASS_SMALL crafting_complexity = FOOD_COMPLEXITY_2 + custom_price = PAYCHECK_CREW /obj/item/food/raw_headcheese name = "raw headcheese block" diff --git a/code/game/objects/items/food/martian.dm b/code/game/objects/items/food/martian.dm index 2441ac0f67478..7ceaf1878176c 100644 --- a/code/game/objects/items/food/martian.dm +++ b/code/game/objects/items/food/martian.dm @@ -732,6 +732,7 @@ foodtypes = MEAT | VEGETABLES | FRUIT | PINEAPPLE w_class = WEIGHT_CLASS_SMALL crafting_complexity = FOOD_COMPLEXITY_4 + custom_price = PAYCHECK_CREW * 1.2 /obj/item/food/salt_chilli_fries name = "salt n' chilli fries" @@ -1210,6 +1211,7 @@ foodtypes = FRUIT | MEAT | PINEAPPLE | VEGETABLES | GRAIN w_class = WEIGHT_CLASS_SMALL crafting_complexity = FOOD_COMPLEXITY_4 //Uses Sambal + custom_price = PAYCHECK_CREW * 2 /obj/item/food/frickles name = "frickles" diff --git a/code/game/objects/items/food/meatdish.dm b/code/game/objects/items/food/meatdish.dm index b9a6c34df04ed..b7a4ca9991619 100644 --- a/code/game/objects/items/food/meatdish.dm +++ b/code/game/objects/items/food/meatdish.dm @@ -554,6 +554,7 @@ w_class = WEIGHT_CLASS_SMALL venue_value = FOOD_PRICE_CHEAP crafting_complexity = FOOD_COMPLEXITY_2 + custom_price = PAYCHECK_CREW * 0.6 /obj/item/food/sausage/make_processable() AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/salami, 6, 3 SECONDS, table_required = TRUE, screentip_verb = "Slice") @@ -734,6 +735,7 @@ foodtypes = MEAT | DAIRY | GRAIN w_class = WEIGHT_CLASS_TINY crafting_complexity = FOOD_COMPLEXITY_3 + custom_price = PAYCHECK_CREW /obj/item/food/bbqribs name = "bbq ribs" diff --git a/code/game/objects/items/food/sandwichtoast.dm b/code/game/objects/items/food/sandwichtoast.dm index c6488f67a1ed5..e440a1039e6d1 100644 --- a/code/game/objects/items/food/sandwichtoast.dm +++ b/code/game/objects/items/food/sandwichtoast.dm @@ -152,6 +152,7 @@ w_class = WEIGHT_CLASS_SMALL venue_value = FOOD_PRICE_CHEAP crafting_complexity = FOOD_COMPLEXITY_3 + custom_price = PAYCHECK_CREW * 0.7 // Used for unit tests, do not delete /obj/item/food/hotdog/debug @@ -174,6 +175,7 @@ w_class = WEIGHT_CLASS_SMALL venue_value = FOOD_PRICE_NORMAL crafting_complexity = FOOD_COMPLEXITY_4 + custom_price = PAYCHECK_CREW /obj/item/food/sandwich/blt name = "\improper BLT" diff --git a/code/game/objects/items/piggy_bank.dm b/code/game/objects/items/piggy_bank.dm new file mode 100644 index 0000000000000..6a9ee494a22ff --- /dev/null +++ b/code/game/objects/items/piggy_bank.dm @@ -0,0 +1,129 @@ +/** + * Piggy banks. They store your hard-earned money until you or someone destroys it. + * If the persistence id is set, money will be carried between rounds until broken. + */ +/obj/item/piggy_bank + name = "piggy bank" + desc = "A pig-shaped money container made of porkelain, oink. Do not throw." //pun very intended. + icon = 'icons/obj/fluff/general.dmi' + icon_state = "piggy_bank" + max_integrity = 8 + w_class = WEIGHT_CLASS_NORMAL + force = 12 + throwforce = 15 + throw_speed = 3 + throw_range = 7 + greyscale_config = /datum/greyscale_config/piggy_bank + ///Some piggy banks are persistent, meaning they carry dosh between rounds. + var/persistence_id + ///Callback to execute upon roundend to save the current amount of cash it has stored, IF persistent. + var/datum/callback/persistence_cb + ///How much dosh can this piggy bank hold. + var/maximum_value = PAYCHECK_COMMAND * 20 + ///How much dosh this piggy bank spawns with. + var/initial_value = 0 + +/obj/item/piggy_bank/Initialize(mapload) + if(!greyscale_colors) + greyscale_colors = pick(COLOR_PINK, + COLOR_LIGHT_ORANGE, + COLOR_GREEN_GRAY, + COLOR_PALE_BLUE_GRAY, + COLOR_DARK_MODERATE_LIME_GREEN, + COLOR_OFF_WHITE, + ) + + . = ..() + + AddElement(/datum/element/can_shatter, shattering_sound = SFX_SHATTER, shatters_as_weapon = TRUE) + AddElement(/datum/element/beauty, 500) + if(!persistence_id) + if(initial_value) + new /obj/item/holochip(src, initial_value) + return + + SSpersistence.load_piggy_bank(src) + persistence_cb = CALLBACK(src, PROC_REF(save_cash)) + SSticker.OnRoundend(persistence_cb) + + if(initial_value & initial_value + calculate_dosh_amount() <= maximum_value) + new /obj/item/holochip(src, initial_value) + +/obj/item/piggy_bank/proc/save_cash() + SSpersistence.save_piggy_bank(src) + +/obj/item/piggy_bank/Destroy() + if(persistence_cb) + LAZYREMOVE(SSticker.round_end_events, persistence_cb) //cleanup the callback. + persistence_cb = null + return ..() + +/obj/item/piggy_bank/deconstruct(disassembled = TRUE) + for(var/obj/item/thing as anything in contents) + thing.forceMove(loc) + //Smashing the piggy after the round is over doesn't count. + if(persistence_id && SSticker.current_state < GAME_STATE_FINISHED) + LAZYADD(SSpersistence.queued_broken_piggy_ids, persistence_id) + return ..() + +/obj/item/piggy_bank/attack_self(mob/user, modifiers) + . = ..() + if(DOING_INTERACTION_WITH_TARGET(user, src)) + return + balloon_alert(user, "rattle rattle...") + if(!do_after(user, 0.5 SECONDS, src)) + return + var/percentile = round(calculate_dosh_amount()/maximum_value * 100, 1) + if(percentile >= 10) + playsound(src, SFX_RATTLE, percentile * 0.5, FALSE, FALSE) + switch(percentile) + if(0) + balloon_alert(user, "it's empty") + if(1 to 9) + balloon_alert(user, "it's almost empty") + if(10 to 25) + balloon_alert(user, "it's some cash") + if(25 to 45) + balloon_alert(user, "it's plenty of cash") + if(45 to 70) + balloon_alert(user, "it feels almost full") + if(70 to 95) + balloon_alert(user, "it feels full") + if(95 to INFINITY) + balloon_alert(user, "brimming with cash") + +/obj/item/piggy_bank/attackby(obj/item/item, mob/user, params) + var/creds_value = item.get_item_credit_value() + if(isnull(creds_value)) + return ..() + + var/dosh_amount = calculate_dosh_amount() + + if(dosh_amount >= maximum_value) + balloon_alert(user, "it's full!") + else if(dosh_amount + creds_value > maximum_value) + balloon_alert(user, "too much cash!") + else if(!user.transferItemToLoc(item, src)) + balloon_alert(user, "stuck in your hands!") + else + balloon_alert(user, "inserted [creds_value] creds") + return TRUE + +///Returns the total amount of credits that its contents amount to. +/obj/item/piggy_bank/proc/calculate_dosh_amount() + var/total_value = 0 + for(var/obj/item/item in contents) + total_value += item.get_item_credit_value() + return total_value + +/obj/item/piggy_bank/museum + name = "Pigston Swinelord VI" + desc = "The museum's mascot piggy bank and favorite embezzler, known to carry donations between shifts without paying taxes. The space IRS hates him." + persistence_id = "museum_piggy" + greyscale_colors = COLOR_PINK + maximum_value = PAYCHECK_COMMAND * 100 + initial_value = PAYCHECK_COMMAND * 4 + +/obj/item/piggy_bank/museum/Initialize(mapload) + . = ..() + AddComponent(/datum/component/areabound) //do not steal. diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index ca669a9733e12..e2d1b6f26e904 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -44,7 +44,8 @@ /obj/item/toy/plush/Initialize(mapload) . = ..() AddComponent(/datum/component/squeak, squeak_override) - AddElement(/datum/element/bed_tuckable, 6, -5, 90) + AddElement(/datum/element/bed_tuckable, mapload, 6, -5, 90) + AddElement(/datum/element/toy_talk) //have we decided if Pinocchio goes in the blue or pink aisle yet? if(gender == NEUTER) diff --git a/code/game/objects/items/puzzle_pieces.dm b/code/game/objects/items/puzzle_pieces.dm index 7ac22d00897ea..dca3fe172159a 100644 --- a/code/game/objects/items/puzzle_pieces.dm +++ b/code/game/objects/items/puzzle_pieces.dm @@ -289,28 +289,47 @@ // literally just buttons // -/obj/machinery/puzzle_button - name = "control panel" - desc = "A panel that controls something nearby. I'm sure it being covered in hazard stripes is fine." +/obj/machinery/puzzle + name = "abstract puzzle gizmo" icon = 'icons/obj/machines/wallmounts.dmi' - icon_state = "lockdown0" resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF - base_icon_state = "lockdown" /// have we been pressed already? var/used = FALSE /// can we be pressed only once? var/single_use = TRUE /// puzzle id we send on press - var/id = "0" //null would literally open every puzzle door without an id + var/id //null would literally open every puzzle door without an id /// queue size, must match count of objects this activates! var/queue_size = 2 + /// should the puzzle machinery perform the final step of the queue link on LateInitialize? An alternative to queue size + var/late_initialize_pop = FALSE -/obj/machinery/puzzle_button/Initialize(mapload) +/obj/machinery/puzzle/Initialize(mapload) . = ..() if(!isnull(id)) - SSqueuelinks.add_to_queue(src, id, queue_size) + SSqueuelinks.add_to_queue(src, id, late_initialize_pop ? 0 : queue_size) + return late_initialize_pop ? INITIALIZE_HINT_LATELOAD : . + +/obj/machinery/puzzle/LateInitialize() + . = ..() + if(late_initialize_pop && id && SSqueuelinks.queues[id]) + SSqueuelinks.pop_link(id) + +/obj/machinery/puzzle/proc/on_puzzle_complete() //incase someone wants to make this do something else for some reason + SEND_SIGNAL(src, COMSIG_PUZZLE_COMPLETED) + +/obj/machinery/puzzle/update_icon_state() + icon_state = "[base_icon_state][used]" + return ..() + +/obj/machinery/puzzle/button + name = "control panel" + desc = "A panel that controls something nearby. I'm sure it being covered in hazard stripes is fine." + icon = 'icons/obj/machines/wallmounts.dmi' + icon_state = "lockdown0" + base_icon_state = "lockdown" -/obj/machinery/puzzle_button/attack_hand(mob/user, list/modifiers) +/obj/machinery/puzzle/button/attack_hand(mob/user, list/modifiers) . = ..() if(.) return @@ -320,37 +339,17 @@ update_icon_state() visible_message(span_notice("[user] presses a button on [src]."), span_notice("You press a button on [src].")) playsound(src, 'sound/machines/terminal_button07.ogg', 45, TRUE) - open_doors() + on_puzzle_complete() -/obj/machinery/puzzle_button/proc/open_doors() //incase someone wants to make this do something else for some reason - SEND_SIGNAL(src, COMSIG_PUZZLE_COMPLETED) - -/obj/machinery/puzzle_button/update_icon_state() - icon_state = "[base_icon_state][used]" - return ..() +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/button, 32) -MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle_button, 32) - -/obj/machinery/puzzle_keycardpad +/obj/machinery/puzzle/keycardpad name = "keycard panel" desc = "A panel that controls something nearby. Accepts keycards." - icon = 'icons/obj/machines/wallmounts.dmi' icon_state = "keycardpad0" - resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | ACID_PROOF | LAVA_PROOF base_icon_state = "keycardpad" - /// were we used successfully? - var/used = FALSE - /// puzzle id we send if the correct card is swiped - var/id = "0" - /// queue size, must match count of objects this activates! - var/queue_size = 2 - -/obj/machinery/puzzle_keycardpad/Initialize(mapload) - . = ..() - if(!isnull(id)) - SSqueuelinks.add_to_queue(src, id, queue_size) -/obj/machinery/puzzle_keycardpad/attackby(obj/item/attacking_item, mob/user, params) +/obj/machinery/puzzle/keycardpad/attackby(obj/item/attacking_item, mob/user, params) . = ..() if(!istype(attacking_item, /obj/item/keycard) || used) return @@ -363,13 +362,75 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle_button, 32) used = TRUE update_icon_state() playsound(src, 'sound/machines/beep.ogg', 45, TRUE) - SEND_SIGNAL(src, COMSIG_PUZZLE_COMPLETED) + on_puzzle_complete() + +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/keycardpad, 32) + +/obj/machinery/puzzle/password + name = "password panel" + desc = "A panel that controls something nearby. This one requires a (case-sensitive) password, and it's not \"Swordfish\"." + icon_state = "passpad0" + base_icon_state = "passpad" + ///The password to this door. + var/password = "" + ///The text shown in the tgui input popup + var/tgui_text = "Please enter the password." + ///The title of the tgui input popup + var/tgui_title = "What's the password?" + ///Decides whether the max length of the input is MAX_NAME_LEN or the length of the password. + var/input_max_len_is_pass = FALSE + +/obj/machinery/puzzle/password/interact(mob/user, list/modifiers) + if(used && single_use) + return + if(!user.can_perform_action(src, ALLOW_SILICON_REACH) || !user.can_interact_with(src)) + return + var/pass_input = tgui_input_text(user, tgui_text, tgui_title, max_length = input_max_len_is_pass ? length(password) : MAX_NAME_LEN) + if(isnull(pass_input) || !user.can_perform_action(src, ALLOW_SILICON_REACH) || !user.can_interact_with(src)) + return + var/correct = pass_input == password + balloon_alert_to_viewers("[correct ? "correct" : "wrong"] password[correct ? "" : "!"]") + if(!correct) + playsound(src, 'sound/machines/buzz-sigh.ogg', 45, TRUE) + return + used = single_use + update_icon_state() + playsound(src, 'sound/machines/terminal_button07.ogg', 45, TRUE) + on_puzzle_complete() -/obj/machinery/puzzle_keycardpad/update_icon_state() - icon_state = "[base_icon_state][used]" - return ..() +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/password, 32) -MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle_keycardpad, 32) +/obj/machinery/puzzle/password/pin + desc = "A panel that controls something nearby. This one requires a PIN password, so let's start by typing in 1234..." + tgui_text = "Please enter the PIN code." + tgui_title = "What's the PIN code?" + input_max_len_is_pass = TRUE + ///The length of the PIN. Suggestion: something between 4 and 12. + var/pin_length = 6 + ///associate a color to each digit that may be found in the password. + var/list/digit_to_color = list() + +/obj/machinery/puzzle/password/pin/Initialize(mapload) + . = ..() + + for(var/iteration in 1 to pin_length) + password += "[rand(1, 9)]" + + var/list/possible_colors = list( + "white", + "black", + "red", + "green", + "blue", + "yellow", + "orange", + "brown", + "gray", + ) + for(var/digit in 0 to 9) + digit_to_color["[digit]"] = pick_n_take(possible_colors) + +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/password/pin, 32) // // blockade @@ -445,7 +506,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle_keycardpad, 32) if(isnull(id) || isnull(queue_id)) log_mapping("[src] id:[id] has no id or door id and has been deleted") return INITIALIZE_HINT_QDEL - + SSqueuelinks.add_to_queue(src, queue_id) /obj/effect/puzzle_poddoor_open/MatchedLinks(id, list/partners) @@ -461,3 +522,99 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle_keycardpad, 32) if(isnull(openclose)) openclose = door.density INVOKE_ASYNC(door, openclose ? TYPE_PROC_REF(/obj/machinery/door/poddoor, open) : TYPE_PROC_REF(/obj/machinery/door/poddoor, close)) + +#define MAX_PUZZLE_DOTS_PER_ROW 4 +#define PUZZLE_DOTS_VERTICAL_OFFSET 7 +#define PUZZLE_DOTS_HORIZONTAL_OFFSET 7 + +///A dotted board that can be used as clue for PIN puzzle machinery +/obj/effect/decal/puzzle_dots + name = "dotted board" + desc = "A board filled with colored dots. What could this mean?" + icon = 'icons/obj/fluff/puzzle_small.dmi' + icon_state = "puzzle_dots" + plane = GAME_PLANE //visible over walls + resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | UNACIDABLE | LAVA_PROOF + flags_1 = UNPAINTABLE_1 + ///The id of the puzzle we're linked to. + var/id + +/obj/effect/decal/puzzle_dots/Initialize(mapload) + . = ..() + if(id) + SSqueuelinks.add_to_queue(src, id) + +/obj/effect/decal/puzzle_dots/MatchedLinks(id, partners) + var/obj/machinery/puzzle/password/pin/pad = locate() in partners + var/list/pass_digits = splittext(pad.password, "") + var/pass_len = length(pass_digits) + var/extra_rows = CEILING((pass_len/MAX_PUZZLE_DOTS_PER_ROW)-1, 1) + if(extra_rows) + pixel_y += round(extra_rows*(PUZZLE_DOTS_VERTICAL_OFFSET*0.5)) + for(var/i in 1 to extra_rows) + var/mutable_appearance/row = mutable_appearance(icon, icon_state) + row.pixel_y = -i*PUZZLE_DOTS_VERTICAL_OFFSET + add_overlay(row) + for(var/i in 1 to pass_len) + var/mutable_appearance/colored_dot = mutable_appearance(icon, "puzzle_dot_single") + colored_dot.color = pad.digit_to_color[pass_digits[i]] + colored_dot.pixel_x = PUZZLE_DOTS_HORIZONTAL_OFFSET * ((i-1)%MAX_PUZZLE_DOTS_PER_ROW) + colored_dot.pixel_y -= CEILING((i/MAX_PUZZLE_DOTS_PER_ROW)-1, 1)*PUZZLE_DOTS_VERTICAL_OFFSET + add_overlay(colored_dot) + +#undef MAX_PUZZLE_DOTS_PER_ROW +#undef PUZZLE_DOTS_VERTICAL_OFFSET +#undef PUZZLE_DOTS_HORIZONTAL_OFFSET + + +/obj/effect/decal/cleanable/crayon/puzzle + name = "Password character" + icon_state = "0" + ///The id of the puzzle we're linked to. + var/puzzle_id + +/obj/effect/decal/cleanable/crayon/puzzle/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null) + . = ..() + name = "number" + if(puzzle_id) + SSqueuelinks.add_to_queue(src, puzzle_id) + +/obj/effect/decal/cleanable/crayon/puzzle/MatchedLinks(id, partners) + var/obj/machinery/puzzle/password/pad = locate() in partners + var/list/pass_character = splittext(pad.password, "") + var/chosen_character = icon_state + if(!findtext(chosen_character, GLOB.is_alphanumeric)) + qdel(src) + return FALSE + icon_state = pick(pass_character) + if(!text2num(icon_state)) + name = "letter" + desc = "A letter vandalizing the station." + return TRUE + +/obj/effect/decal/cleanable/crayon/puzzle/pin + name = "PIN number" + +/obj/effect/decal/cleanable/crayon/puzzle/pin/MatchedLinks(id, partners) + . = ..() + var/obj/machinery/puzzle/password/pin/pad = locate() in partners + add_atom_colour(pad.digit_to_color[icon_state], FIXED_COLOUR_PRIORITY) + +/obj/item/paper/fluff/scrambled_pass + name = "gibberish note" + icon_state = "scrap" + ///The ID associated to the puzzle we're part of. + var/puzzle_id + +/obj/item/paper/fluff/scrambled_pass/Initialize(mapload) + . = ..() + if(mapload && puzzle_id) + SSqueuelinks.add_to_queue(src, puzzle_id) + +/obj/item/paper/fluff/scrambled_pass/MatchedLinks(id, partners) + var/obj/machinery/puzzle/password/pad = locate() in partners + var/scrambled_text = "" + var/list/pass_characters = splittext(pad.password, "") + for(var/i in 1 to rand(200, 300)) + scrambled_text += pick(pass_characters) + add_raw_text(scrambled_text) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 306bc0fa9f942..1f33384b39337 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -310,6 +310,7 @@ name = "improvised gauze" singular_name = "improvised gauze" desc = "A roll of cloth roughly cut from something that does a decent job of stabilizing wounds, but less efficiently so than real medical gauze." + icon_state = "gauze_imp" self_delay = 6 SECONDS other_delay = 3 SECONDS splint_factor = 0.85 diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index e042ad4c01cb3..3b32e6b1d9356 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -48,6 +48,11 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \ new /datum/stack_recipe("bench (left)", /obj/structure/chair/sofa/bench/left, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ new /datum/stack_recipe("bench (right)", /obj/structure/chair/sofa/bench/right, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ new /datum/stack_recipe("bench (corner)", /obj/structure/chair/sofa/bench/corner, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ + new /datum/stack_recipe("tram bench (solo)", /obj/structure/chair/sofa/bench/tram/solo, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ + new /datum/stack_recipe("tram bench (middle)", /obj/structure/chair/sofa/bench/tram, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ + new /datum/stack_recipe("tram bench (left)", /obj/structure/chair/sofa/bench/tram/left, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ + new /datum/stack_recipe("tram bench (right)", /obj/structure/chair/sofa/bench/tram/right, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ + new /datum/stack_recipe("tram bench (corner)", /obj/structure/chair/sofa/bench/tram/corner, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE), \ )), \ new /datum/stack_recipe_list("chess pieces", list( \ new /datum/stack_recipe("White Pawn", /obj/structure/chess/whitepawn, 2, time = 1 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_ENTERTAINMENT), \ @@ -779,7 +784,8 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ ) GLOBAL_LIST_INIT(plastic_recipes, list( new /datum/stack_recipe("plastic floor tile", /obj/item/stack/tile/plastic, 1, 4, 20, time = 2 SECONDS, check_density = FALSE, category = CAT_TILES), \ - new /datum/stack_recipe("thermoplastic tram tile", /obj/item/stack/thermoplastic, 1, 2, time = 4 SECONDS, check_density = FALSE, placement_checks = STACK_CHECK_TRAM_EXCLUSIVE, category = CAT_TILES), \ + new /datum/stack_recipe("light tram tile", /obj/item/stack/thermoplastic/light, 1, 4, 20, time = 2 SECONDS, check_density = FALSE, category = CAT_TILES), \ + new /datum/stack_recipe("dark tram tile", /obj/item/stack/thermoplastic, 1, 4, 20, time = 2 SECONDS, check_density = FALSE, category = CAT_TILES), \ new /datum/stack_recipe("folding plastic chair", /obj/structure/chair/plastic, 2, check_density = FALSE, category = CAT_FURNITURE), \ new /datum/stack_recipe("plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_solid_ground = TRUE, time = 4 SECONDS, category = CAT_FURNITURE), \ new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/cup/glass/waterbottle/empty, check_density = FALSE, category = CAT_CONTAINERS), \ diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 429acfb9ce966..217a07500064d 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -10,6 +10,7 @@ #define KIT_SNIPER "sniper" #define KIT_NUKEOPS_METAGAME "metaops" #define KIT_LORD_SINGULOTH "lordsingulo" +#define KIT_REVOLUTIONARY "revolutionary" #define KIT_JAMES_BOND "bond" #define KIT_NINJA "ninja" @@ -19,6 +20,8 @@ #define KIT_BEES "bee" #define KIT_MR_FREEZE "mr_freeze" #define KIT_TRAITOR_2006 "ancient" +#define KIT_SAM_FISHER "sam_fisher" +#define KIT_PROP_HUNT "prop_hunt" /// last audited december 2022 /obj/item/storage/box/syndicate @@ -35,7 +38,8 @@ KIT_IMPLANTS = 1, KIT_HACKER = 3, KIT_SNIPER = 1, - KIT_NUKEOPS_METAGAME = 1 + KIT_NUKEOPS_METAGAME = 1, + KIT_REVOLUTIONARY = 2 ))) if(KIT_RECON) new /obj/item/clothing/glasses/thermal/xray(src) // ~8 tc? @@ -165,6 +169,18 @@ new /obj/item/card/emag(src) // 4 tc new /obj/item/card/emag/doorjack(src) // 3 tc + if(KIT_REVOLUTIONARY) + new /obj/item/healthanalyzer/rad_laser(src) // 3 TC + new /obj/item/assembly/flash/hypnotic(src) // 7 TC + new /obj/item/storage/pill_bottle/lsd(src) // ~1 TC + new /obj/item/pen/sleepy(src) // 4 TC + new /obj/item/gun/ballistic/revolver/nagant(src) // 13 TC comparable to 357. revolvers + new /obj/item/megaphone(src) + new /obj/item/bedsheet/rev(src) + new /obj/item/clothing/suit/armor/vest/russian_coat(src) + new /obj/item/clothing/head/helmet/rus_ushanka(src) + new /obj/item/storage/box/syndie_kit/poster_box(src) + /obj/item/storage/box/syndicate/bundle_b/PopulateContents() switch (pick_weight(list( KIT_JAMES_BOND = 2, @@ -174,7 +190,9 @@ KIT_MAD_SCIENTIST = 2, KIT_BEES = 1, KIT_MR_FREEZE = 2, - KIT_TRAITOR_2006 = 1 + KIT_TRAITOR_2006 = 1, + KIT_SAM_FISHER = 1, + KIT_PROP_HUNT = 1 ))) if(KIT_JAMES_BOND) new /obj/item/gun/ballistic/automatic/pistol(src) // 7 tc @@ -261,9 +279,26 @@ new /obj/item/gun/energy/laser/thermal/cryo(src) // ~6 tc new /obj/item/melee/energy/sword/saber/blue(src) //see see it fits the theme bc its blue and ice is blue, 8 tc - if(KIT_TRAITOR_2006) //A kit so old, it's probably older than you. //This bundle is filled with the entire unlink contents traitors had access to in 2006, from OpenSS13. Notably the esword was not a choice but existed in code. + if(KIT_TRAITOR_2006) //A kit so old, it's probably older than you. //This bundle is filled with the entire uplink contents traitors had access to in 2006, from OpenSS13. Notably the esword was not a choice but existed in code. new /obj/item/storage/toolbox/emergency/old/ancientbundle(src) //Items fit neatly into a classic toolbox just to remind you what the theme is. + if(KIT_SAM_FISHER) + new /obj/item/clothing/under/syndicate/combat(src) + new /obj/item/clothing/suit/armor/vest/marine/pmc(src) //The armor kit is comparable to the infiltrator, 6 TC + new /obj/item/clothing/head/helmet/marine/pmc(src) + new /obj/item/clothing/mask/gas/sechailer(src) + new /obj/item/clothing/glasses/night(src) // 3~ TC + new /obj/item/clothing/gloves/krav_maga/combatglovesplus(src) //5TC + new /obj/item/clothing/shoes/jackboots(src) + new /obj/item/storage/belt/military/assault/fisher(src) //items in this belt easily costs 18 TC + + if(KIT_PROP_HUNT) + new /obj/item/chameleon(src) // 7 TC + new /obj/item/card/emag/doorjack(src) // 3 TC + new /obj/item/storage/box/syndie_kit/imp_stealth(src) //8 TC + new /obj/item/gun/ballistic/automatic/pistol(src) // 7 TC + new /obj/item/clothing/glasses/thermal(src) // 4 TC + /obj/item/storage/toolbox/emergency/old/ancientbundle/ //So the subtype works /obj/item/storage/toolbox/emergency/old/ancientbundle/PopulateContents() @@ -276,6 +311,17 @@ new /obj/item/implanter/freedom(src) // 5 tc new /obj/item/stack/telecrystal(src) //The failsafe/self destruct isn't an item we can physically include in the kit, but 1 TC is technically enough to buy the equivalent. +/obj/item/storage/belt/military/assault/fisher + +/obj/item/storage/belt/military/assault/fisher/PopulateContents() + new /obj/item/gun/ballistic/automatic/pistol/clandestine(src) // 7 TC + new /obj/item/suppressor(src) // 3 TC + new /obj/item/ammo_box/magazine/m10mm(src) // 1 TC + new /obj/item/ammo_box/magazine/m10mm(src) + new /obj/item/gun/energy/recharge/fisher(src) // Acquirable through black market, shit utility item 1 TC + new /obj/item/card/emag/doorjack(src) // 3 TC + new /obj/item/knife/combat(src) //comparable to the e-dagger, 2 TC + /obj/item/storage/box/syndie_kit name = "box" desc = "A sleek, sturdy box." @@ -807,6 +853,7 @@ #undef KIT_SNIPER #undef KIT_NUKEOPS_METAGAME #undef KIT_LORD_SINGULOTH +#undef KIT_REVOLUTIONARY #undef KIT_JAMES_BOND #undef KIT_NINJA @@ -816,3 +863,5 @@ #undef KIT_BEES #undef KIT_MR_FREEZE #undef KIT_TRAITOR_2006 +#undef KIT_SAM_FISHER +#undef KIT_PROP_HUNT diff --git a/code/game/objects/items/toy_mechs.dm b/code/game/objects/items/toy_mechs.dm index ce3ce4600c734..c50908738fe94 100644 --- a/code/game/objects/items/toy_mechs.dm +++ b/code/game/objects/items/toy_mechs.dm @@ -53,6 +53,7 @@ /obj/item/toy/mecha/Initialize(mapload) . = ..() AddElement(/datum/element/series, /obj/item/toy/mecha, "Mini-Mecha action figures") + AddElement(/datum/element/toy_talk) combat_health = max_combat_health switch(special_attack_type) if(SPECIAL_ATTACK_DAMAGE) @@ -263,12 +264,8 @@ if(wins || losses) . += span_notice("This toy has [wins] wins, and [losses] losses.") -/** - * Override the say proc if they're mute - */ -/obj/item/toy/mecha/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - if(!quiet) - . = ..() +/obj/item/toy/mecha/can_speak(allow_mimes) + return !quiet && ..() /** * The 'master' proc of the mech battle. Processes the entire battle's events and makes sure it start and finishes correctly. diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index c91f6eb6c4401..1d2b0bba7b134 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1013,6 +1013,7 @@ /obj/item/toy/figure/Initialize(mapload) . = ..() desc = "A \"Space Life\" brand [src]." + AddElement(/datum/element/toy_talk) /obj/item/toy/figure/attack_self(mob/user as mob) if(cooldown <= world.time) @@ -1245,13 +1246,9 @@ to_chat(user, span_notice("You name the dummy as \"[doll_name]\".")) name = "[initial(name)] - [doll_name]" -/obj/item/toy/dummy/talk_into(atom/movable/A, message, channel, list/spans, datum/language/language, list/message_mods) - var/mob/M = A - if (istype(M)) - M.log_talk(message, LOG_SAY, tag="dummy toy") - - say(message, language, sanitize = FALSE) - return NOPASS +/obj/item/toy/dummy/Initialize(mapload) + . = ..() + AddElement(/datum/element/toy_talk) /obj/item/toy/dummy/GetVoice() return doll_name diff --git a/code/game/objects/items/vending_items.dm b/code/game/objects/items/vending_items.dm index 0383767ce66e8..7084b313dff59 100644 --- a/code/game/objects/items/vending_items.dm +++ b/code/game/objects/items/vending_items.dm @@ -19,8 +19,10 @@ w_class = WEIGHT_CLASS_BULKY armor_type = /datum/armor/item_vending_refill - // Built automatically from the corresponding vending machine. - // If null, considered to be full. Otherwise, is list(/typepath = amount). + /** + * Built automatically from the corresponding vending machine. + * If null, considered to be full. Otherwise, is list(/typepath = amount). + */ var/list/products var/list/product_categories var/list/contraband diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 059f78b80c90d..f72199217653f 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -554,124 +554,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/ectoplasm/mystic icon_state = "mysticplasm" -/obj/item/statuebust - name = "bust" - desc = "A priceless ancient marble bust, the kind that belongs in a museum." //or you can hit people with it - icon = 'icons/obj/art/statue.dmi' - icon_state = "bust" - force = 15 - throwforce = 10 - throw_speed = 5 - throw_range = 2 - attack_verb_continuous = list("busts") - attack_verb_simple = list("bust") - var/impressiveness = 45 - -/obj/item/statuebust/Initialize(mapload) - . = ..() - AddElement(/datum/element/art, impressiveness) - AddElement(/datum/element/beauty, 1000) - -/obj/item/statuebust/hippocratic - name = "hippocrates bust" - desc = "A bust of the famous Greek physician Hippocrates of Kos, often referred to as the father of western medicine." - icon_state = "hippocratic" - impressiveness = 50 - // If it hits the prob(reference_chance) chance, this is set to TRUE. Adds medical HUD when wielded, but has a 10% slower attack speed and is too bloody to make an oath with. - var/reference = FALSE - // Chance for above. - var/reference_chance = 1 - // Minimum time inbetween oaths. - COOLDOWN_DECLARE(oath_cd) - -/obj/item/statuebust/hippocratic/evil - reference_chance = 100 - -/obj/item/statuebust/hippocratic/Initialize(mapload) - . = ..() - if(prob(reference_chance)) - name = "Solemn Vow" - desc = "Art lovers will cherish the bust of Hippocrates, commemorating a time when medics still thought doing no harm was a good idea." - attack_speed = CLICK_CD_SLOW - reference = TRUE - -/obj/item/statuebust/hippocratic/examine(mob/user) - . = ..() - if(reference) - . += span_notice("You could activate the bust in-hand to swear or forswear a Hippocratic Oath... but it seems like somebody decided it was more of a Hippocratic Suggestion. This thing is caked with bits of blood and gore.") - return - . += span_notice("You can activate the bust in-hand to swear or forswear a Hippocratic Oath! This has no effects except pacifism or bragging rights. Does not remove other sources of pacifism. Do not eat.") - -/obj/item/statuebust/hippocratic/equipped(mob/living/carbon/human/user, slot) - ..() - if(!(slot & ITEM_SLOT_HANDS)) - return - var/datum/atom_hud/our_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - our_hud.show_to(user) - ADD_TRAIT(user, TRAIT_MEDICAL_HUD, type) - -/obj/item/statuebust/hippocratic/dropped(mob/living/carbon/human/user) - ..() - if(HAS_TRAIT_NOT_FROM(user, TRAIT_MEDICAL_HUD, type)) - return - var/datum/atom_hud/our_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - our_hud.hide_from(user) - REMOVE_TRAIT(user, TRAIT_MEDICAL_HUD, type) - -/obj/item/statuebust/hippocratic/attack_self(mob/user) - if(!iscarbon(user)) - to_chat(user, span_warning("You remember how the Hippocratic Oath specifies 'my fellow human beings' and realize that it's completely meaningless to you.")) - return - - if(reference) - to_chat(user, span_warning("As you prepare yourself to swear the Oath, you realize that doing so on a blood-caked bust is probably not a good idea.")) - return - - if(!COOLDOWN_FINISHED(src, oath_cd)) - to_chat(user, span_warning("You've sworn or forsworn an oath too recently to undo your decisions. The bust looks at you with disgust.")) - return - - COOLDOWN_START(src, oath_cd, 5 MINUTES) - - if(HAS_TRAIT_FROM(user, TRAIT_PACIFISM, type)) - to_chat(user, span_warning("You've already sworn a vow. You start preparing to rescind it...")) - if(do_after(user, 5 SECONDS, target = user)) - user.say("Yeah this Hippopotamus thing isn't working out. I quit!", forced = "hippocratic hippocrisy") - REMOVE_TRAIT(user, TRAIT_PACIFISM, type) - - // they can still do it for rp purposes - if(HAS_TRAIT_NOT_FROM(user, TRAIT_PACIFISM, type)) - to_chat(user, span_warning("You already don't want to harm people, this isn't going to do anything!")) - - - to_chat(user, span_notice("You remind yourself of the Hippocratic Oath's contents and prepare to swear yourself to it...")) - if(do_after(user, 4 SECONDS, target = user)) - user.say("I swear to fulfill, to the best of my ability and judgment, this covenant:", forced = "hippocratic oath") - else - return fuck_it_up(user) - if(do_after(user, 2 SECONDS, target = user)) - user.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.", forced = "hippocratic oath") - else - return fuck_it_up(user) - if(do_after(user, 3 SECONDS, target = user)) - user.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.", forced = "hippocratic oath") - else - - return fuck_it_up(user) - if(do_after(user, 3 SECONDS, target = user)) - user.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.", forced = "hippocratic oath") - else - return fuck_it_up(user) - - to_chat(user, span_notice("Contentment, understanding, and purpose washes over you as you finish the oath. You consider for a second the concept of harm and shudder.")) - ADD_TRAIT(user, TRAIT_PACIFISM, type) - -// Bully the guy for fucking up. -/obj/item/statuebust/hippocratic/proc/fuck_it_up(mob/living/carbon/user) - to_chat(user, span_warning("You forget what comes next like a dumbass. The Hippocrates bust looks down on you, disappointed.")) - user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2) - COOLDOWN_RESET(src, oath_cd) - /obj/item/tailclub name = "tail club" desc = "For the beating to death of lizards with their own tails." diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index f80042f5679a7..50fcae8dec138 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -35,7 +35,7 @@ LINEN BINS /obj/item/bedsheet/Initialize(mapload) . = ..() AddComponent(/datum/component/surgery_initiator) - AddElement(/datum/element/bed_tuckable, 0, 0, 0) + AddElement(/datum/element/bed_tuckable, mapload, 0, 0, 0) if(bedsheet_type == BEDSHEET_DOUBLE) stack_amount *= 2 dying_key = DYE_REGISTRY_DOUBLE_BEDSHEET diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index b3d3c7085bd50..d2df088e06f73 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -94,6 +94,11 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /// Volume of the internal air var/air_volume = TANK_STANDARD_VOLUME * 3 + /// How many pixels the closet can shift on the x axis when shaking + var/x_shake_pixel_shift = 2 + /// how many pixels the closet can shift on the y axes when shaking + var/y_shake_pixel_shift = 1 + /datum/armor/structure_closet melee = 20 bullet = 10 @@ -1031,6 +1036,9 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) user.visible_message(span_warning("[src] begins to shake violently!"), \ 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 banging from [src].")) + + addtimer(CALLBACK(src, PROC_REF(check_if_shake)), 1 SECONDS) + if(do_after(user,(breakout_time), target = src)) if(!user || user.stat != CONSCIOUS || user.loc != src || opened || (!locked && !welded) ) return @@ -1045,6 +1053,23 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /obj/structure/closet/relay_container_resist_act(mob/living/user, obj/container) container.container_resist_act() +/// Check if someone is still resisting inside, and choose to either keep shaking or stop shaking the closet +/obj/structure/closet/proc/check_if_shake() + // Assuming we decide to shake again, how long until we check to shake again + var/next_check_time = 1 SECONDS + + // How long we shake between different calls of Shake(), so that it starts shaking and stops, instead of a steady shake + var/shake_duration = 0.3 SECONDS + + for(var/mob/living/mob in contents) + if(DOING_INTERACTION_WITH_TARGET(mob, src)) + // Shake and queue another check_if_shake + Shake(x_shake_pixel_shift, y_shake_pixel_shift, shake_duration, shake_interval = 0.1 SECONDS) + addtimer(CALLBACK(src, PROC_REF(check_if_shake)), next_check_time) + return TRUE + + // If we reach here, nobody is resisting, so dont shake + return FALSE /obj/structure/closet/proc/bust_open() SIGNAL_HANDLER diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index c43a83b085aef..c4f84e9ca3d24 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -18,6 +18,8 @@ drag_slowdown = 0 door_anim_time = 0 // no animation pass_flags_self = PASSSTRUCTURE | LETPASSTHROW + x_shake_pixel_shift = 1 + y_shake_pixel_shift = 2 /// Mobs standing on it are nudged up by this amount. var/elevation = 14 /// The same, but when the crate is open diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index f35b41c53d2a3..3bb24147f5c49 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -410,11 +410,10 @@ max_integrity = 350 /obj/structure/girder/tram - name = "tram frame" + name = "tram girder" desc = "Titanium framework to construct tram walls. Can be plated with titanium glass or other wall materials." icon_state = "tram" state = GIRDER_TRAM - density = FALSE obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN /obj/structure/girder/tram/corner diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index 08a4394346af9..284f5df2a6d61 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -437,8 +437,8 @@ defending_mobs = list( /mob/living/basic/mining/lobstrosity, /mob/living/basic/mining/legion/snow/spawner_made, + /mob/living/basic/mining/wolf, /mob/living/simple_animal/hostile/asteroid/polarbear, - /mob/living/simple_animal/hostile/asteroid/wolf, ) ore_vent_options = list( SMALL_VENT_TYPE, @@ -450,8 +450,8 @@ /mob/living/basic/mining/lobstrosity, /mob/living/basic/mining/legion/snow/spawner_made, /mob/living/basic/mining/ice_demon, + /mob/living/basic/mining/wolf, /mob/living/simple_animal/hostile/asteroid/polarbear, - /mob/living/simple_animal/hostile/asteroid/wolf, ) ore_vent_options = list( SMALL_VENT_TYPE = 3, diff --git a/code/game/objects/structures/tank_holder.dm b/code/game/objects/structures/tank_holder.dm index 9b5b33d8417eb..5b7c9d2ed5534 100644 --- a/code/game/objects/structures/tank_holder.dm +++ b/code/game/objects/structures/tank_holder.dm @@ -142,3 +142,7 @@ /obj/structure/tank_holder/extinguisher/advanced icon_state = "holder_foam_extinguisher" tank = /obj/item/extinguisher/advanced + +/obj/structure/tank_holder/extinguisher/anti + icon_state = "holder_extinguisher" + tank = /obj/item/extinguisher/anti diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index b343cd85f50b1..edf7f2fc803c1 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -760,11 +760,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen, (-16)) open = !open if(open) layer = SIGN_LAYER - set_density(FALSE) set_opacity(FALSE) else layer = WALL_OBJ_LAYER - set_density(TRUE) if(opaque_closed) set_opacity(TRUE) diff --git a/code/game/say.dm b/code/game/say.dm index e6ea129d5a08b..42aea57cf6110 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -21,7 +21,36 @@ GLOBAL_LIST_INIT(freqtospan, list( "[FREQ_CTF_YELLOW]" = "yellowteamradio" )) -/atom/movable/proc/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = FALSE, message_range = 7, datum/saymode/saymode = null) +/** + * What makes things... talk. + * + * * message - The message to say. + * * bubble_type - The type of speech bubble to use when talking + * * spans - A list of spans to attach to the message. Includes the atom's speech span by default + * * sanitize - Should we sanitize the message? Only set to FALSE if you have ALREADY sanitized it + * * language - The language to speak in. Defaults to the atom's selected language + * * ignore_spam - Should we ignore spam checks? + * * forced - What was it forced by? null if voluntary. (NOT a boolean!) + * * filterproof - Do we bypass the filter when checking the message? + * * message_range - The range of the message. Defaults to 7 + * * saymode - Saymode passed to the speech + * This is usually set automatically and is only relevant for living mobs. + * * message_mods - A list of message modifiers, i.e. whispering/singing. + * Most of these are set automatically but you can pass in your own pre-say. + */ +/atom/movable/proc/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!try_speak(message, ignore_spam, forced, filterproof)) return if(sanitize) @@ -31,7 +60,6 @@ GLOBAL_LIST_INIT(freqtospan, list( spans |= speech_span if(!language) language = get_selected_language() - var/list/message_mods = list() message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) send_speech(message, message_range, src, bubble_type, spans, language, message_mods, forced = forced) @@ -60,7 +88,7 @@ GLOBAL_LIST_INIT(freqtospan, list( * TRUE of FASE depending on if our movable can speak */ /atom/movable/proc/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE) - return TRUE + return can_speak() /** * Checks if our movable can currently speak, vocally, in general. @@ -77,7 +105,8 @@ GLOBAL_LIST_INIT(freqtospan, list( * if TRUE, we will check if the movable can speak REGARDLESS of if they have an active mime vow. */ /atom/movable/proc/can_speak(allow_mimes = FALSE) - return TRUE + SHOULD_BE_PURE(TRUE) + return !HAS_TRAIT(src, TRAIT_MUTE) /atom/movable/proc/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language, list/message_mods = list(), forced = FALSE, tts_message, list/tts_filter) var/found_client = FALSE @@ -174,7 +203,15 @@ GLOBAL_LIST_INIT(freqtospan, list( /atom/movable/proc/get_default_say_verb() return verb_say -/atom/movable/proc/say_quote(input, list/spans=list(speech_span), list/message_mods = list()) +/** + * This prock is used to generate a message for chat + * Generates the `says, "meme"` part of the `Grey Tider says, "meme"`. + * + * input - The message to be said + * spans - A list of spans to attach to the message. Includes the atom's speech span by default + * message_mods - A list of message modifiers, i.e. whispering/singing + */ +/atom/movable/proc/say_quote(input, list/spans = list(speech_span), list/message_mods = list()) if(!input) input = "..." diff --git a/code/game/sound.dm b/code/game/sound.dm index e575534bdaeed..17275f5f3a63e 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -424,4 +424,10 @@ 'sound/items/reel4.ogg', 'sound/items/reel5.ogg', ) + if(SFX_RATTLE) + soundin = pick( + 'sound/items/rattle1.ogg', + 'sound/items/rattle2.ogg', + 'sound/items/rattle3.ogg', + ) return soundin diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 8a278c1cc9131..7864b3d732900 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -274,6 +274,10 @@ GLOBAL_PROTECT(admin_verbs_poll) add_verb(src, /client/proc/play_web_sound) if(rights & R_SPAWN) add_verb(src, GLOB.admin_verbs_spawn) +#ifdef MAP_TEST + remove_verb(src, /client/proc/enable_mapping_verbs) + add_verb(src, list(/client/proc/disable_mapping_verbs, GLOB.admin_verbs_debug_mapping)) +#endif /client/proc/remove_admin_verbs() remove_verb(src, list( @@ -1191,4 +1195,3 @@ GLOBAL_PROTECT(admin_verbs_poll) QDEL_NULL(segment.ai_controller) segment.AddComponent(/datum/component/mob_chain, front = previous) previous = segment - diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm index a970c7a5335ef..877bebffe9509 100644 --- a/code/modules/admin/antag_panel.dm +++ b/code/modules/admin/antag_panel.dm @@ -99,6 +99,7 @@ GLOBAL_VAR(antag_prototypes) out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
" out += "Assigned role: [assigned_role.title]. Edit
" out += "Faction and special role: [special_role]
" + out += "Show Teams

" var/special_statuses = get_special_statuses() if(length(special_statuses)) diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index 4d74f5425608f..b65f72f8f4d79 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -87,14 +87,14 @@ if (!possessable.ckey && possessable.stat == CONSCIOUS) // Only assign ghosts to living, non-occupied mobs! bodies += possessable - var/list/candidates = SSpolling.poll_ghost_candidates_for_mobs( - question = "Would you like to be [group_name]?", + var/list/candidates = SSpolling.poll_ghosts_for_targets( + question = "Would you like to be [span_notice(group_name)]?", role = ROLE_SENTIENCE, check_jobban = ROLE_SENTIENCE, poll_time = 10 SECONDS, - mobs = bodies, + checked_targets = bodies, ignore_category = POLL_IGNORE_SHUTTLE_DENIZENS, - pic_source = src, + alert_pic = src, role_name_text = "sentience fun balloon", ) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index e98016df74f3d..9d2525ed8fa2d 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -232,7 +232,7 @@ GLOBAL_PROTECT(href_token) return VALID_2FA_CONNECTION if (!SSdbcore.Connect()) - if (verify_backup_data(client)) + if (verify_backup_data(client) || (client.ckey in GLOB.protected_admins)) return VALID_2FA_CONNECTION else return list(FALSE, null) diff --git a/code/modules/admin/smites/imaginary_friend_special.dm b/code/modules/admin/smites/imaginary_friend_special.dm index 5b2bc6ba80547..e670e26fd1fa4 100644 --- a/code/modules/admin/smites/imaginary_friend_special.dm +++ b/code/modules/admin/smites/imaginary_friend_special.dm @@ -58,7 +58,6 @@ return FALSE var/list/volunteers = SSpolling.poll_ghost_candidates( - question = "Do you want to play as an imaginary friend?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_IMAGINARYFRIEND, diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 126d8756762ca..35b1baa063d57 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -680,8 +680,7 @@ names[name] = ruin_landmark - var/ruinname = input("Select ruin", "Jump to Ruin") as null|anything in sort_list(names) - + var/ruinname = tgui_input_list(usr, "Select ruin", "Jump to Ruin", sort_list(names)) var/obj/effect/landmark/ruin/landmark = names[ruinname] @@ -715,7 +714,7 @@ themed_names[name] = list(ruin, theme, list(ruin.default_area)) names += sort_list(themed_names) - var/ruinname = input("Select ruin", "Spawn Ruin") as null|anything in names + var/ruinname = tgui_input_list(usr, "Select ruin", "Spawn Ruin", sort_list(names)) var/data = names[ruinname] if (!data) return diff --git a/code/modules/admin/verbs/ert.dm b/code/modules/admin/verbs/ert.dm index 8b4c7e3c1b823..6fc238931a65e 100644 --- a/code/modules/admin/verbs/ert.dm +++ b/code/modules/admin/verbs/ert.dm @@ -121,7 +121,7 @@ var/list/spawnpoints = GLOB.emergencyresponseteamspawn var/index = 0 - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for [ertemplate.polldesc]?", check_jobban = "deathsquad", pic_source = /obj/item/card/id/advanced/centcom/ert, role_name_text = "emergency response team") + var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for [span_notice(ertemplate.polldesc)]?", check_jobban = "deathsquad", alert_pic = /obj/item/card/id/advanced/centcom/ert, role_name_text = "emergency response team") var/teamSpawned = FALSE // This list will take priority over spawnpoints if not empty diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index a772f69999271..827bbfb16e862 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -3,8 +3,7 @@ set name = "Map template - Place" var/datum/map_template/template - - var/map = input(src, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in sort_list(SSmapping.map_templates) + var/map = tgui_input_list(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template", sort_list(SSmapping.map_templates)) if(!map) return template = SSmapping.map_templates[map] diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index 20a05685bc9ed..fa2a9fa19c628 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -405,7 +405,7 @@ GLOBAL_DATUM(everyone_an_antag, /datum/everyone_is_an_antag_controller) var/list/candidates = list() if (prefs["offerghosts"]["value"] == "Yes") - candidates = SSpolling.poll_ghost_candidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), check_jobban = ROLE_TRAITOR, pic_source = pathToSpawn, role_name_text = "portal storm") + candidates = SSpolling.poll_ghost_candidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), check_jobban = ROLE_TRAITOR, alert_pic = pathToSpawn, role_name_text = "portal storm") if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"]) message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]") @@ -576,7 +576,7 @@ GLOBAL_DATUM(everyone_an_antag, /datum/everyone_is_an_antag_controller) if(teamsize <= 0) return FALSE - candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a Nanotrasen emergency response drone?", check_jobban = ROLE_DRONE, pic_source = /mob/living/basic/drone/classic, role_name_text = "nanotrasen emergency response drone") + candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a [span_notice("Nanotrasen emergency response drone")]?", check_jobban = ROLE_DRONE, alert_pic = /mob/living/basic/drone/classic, role_name_text = "nanotrasen emergency response drone") if(length(candidates) == 0) return FALSE diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index c679fd45dfa16..89ed48585ed36 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -293,13 +293,12 @@ GLOBAL_LIST_EMPTY(antagonists) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [name]?", check_jobban = "[name]", role = job_rank, poll_time = 5 SECONDS, target_mob = owner.current, pic_source = owner.current, role_name_text = name) - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = job_rank, role = job_rank, poll_time = 5 SECONDS, checked_target = owner.current, alert_pic = owner.current, role_name_text = name) + if(chosen_one) to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner)]) to replace a jobbanned player.") + message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(owner)]) to replace a jobbanned player.") owner.current.ghostize(FALSE) - owner.current.key = C.key + owner.current.key = chosen_one.key /** * Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion. diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index 9ef40a9cebfd9..19ff29651efae 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -55,16 +55,15 @@ /obj/item/antag_spawner/contract/proc/poll_for_student(mob/living/carbon/human/teacher, apprentice_school) balloon_alert(teacher, "contacting apprentice...") polling = TRUE - var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a wizard's [apprentice_school] apprentice?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 15 SECONDS, target_mob = src, pic_source = teacher, role_name_text = "wizard apprentice") + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger("[teacher]'s")] [span_notice("[apprentice_school] apprentice")]?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 15 SECONDS, checked_target = src, alert_pic = /obj/item/clothing/head/wizard/red, jump_target = src, role_name_text = "wizard apprentice", chat_text_border_icon = /obj/item/clothing/head/wizard/red) polling = FALSE - if(!LAZYLEN(candidates)) + if(isnull(chosen_one)) to_chat(teacher, span_warning("Unable to reach your apprentice! You can either attack the spellbook with the contract to refund your points, or wait and try again later.")) return if(QDELETED(src) || used) return used = TRUE - var/mob/dead/observer/student = pick(candidates) - spawn_antag(student.client, get_turf(src), apprentice_school, teacher.mind) + spawn_antag(chosen_one.client, get_turf(src), apprentice_school, teacher.mind) /obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, kind, datum/mind/user) new /obj/effect/particle_effect/fluid/smoke(T) @@ -134,13 +133,12 @@ return to_chat(user, span_notice("You activate [src] and wait for confirmation.")) - var/list/nuke_candidates = SSpolling.poll_ghost_candidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", check_jobban = ROLE_OPERATIVE, role = ROLE_OPERATIVE, poll_time = 15 SECONDS, ignore_category = POLL_IGNORE_SYNDICATE, pic_source = src, role_name_text = "syndicate [borg_to_spawn ? "[borg_to_spawn] cyborg":"operative"]") - if(LAZYLEN(nuke_candidates)) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_OPERATIVE, role = ROLE_OPERATIVE, poll_time = 15 SECONDS, ignore_category = POLL_IGNORE_SYNDICATE, alert_pic = src, role_name_text = "syndicate [borg_to_spawn ? "[borg_to_spawn] cyborg":"operative"]", amount_to_pick = 1) + if(chosen_one) if(QDELETED(src) || !check_usability(user)) return used = TRUE - var/mob/dead/observer/G = pick(nuke_candidates) - spawn_antag(G.client, get_turf(src), "nukeop", user.mind) + spawn_antag(chosen_one.client, get_turf(src), "nukeop", user.mind) do_sparks(4, TRUE, src) qdel(src) else @@ -252,14 +250,13 @@ return if(used) return - var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [initial(demon_type.name)]?", check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, poll_time = 5 SECONDS, target_mob = src, pic_source = src, role_name_text = initial(demon_type.name)) - if(LAZYLEN(candidates)) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, poll_time = 5 SECONDS, checked_target = src, alert_pic = demon_type, jump_target = src, role_name_text = initial(demon_type.name)) + if(chosen_one) if(used || QDELETED(src)) return used = TRUE - var/mob/dead/observer/summoned = pick(candidates) - user.log_message("has summoned forth the [initial(demon_type.name)] (played by [key_name(summoned)]) using a [name].", LOG_GAME) // has to be here before we create antag otherwise we can't get the ckey of the demon - spawn_antag(summoned.client, get_turf(src), initial(demon_type.name), user.mind) + user.log_message("has summoned forth the [initial(demon_type.name)] (played by [key_name(chosen_one)]) using a [name].", LOG_GAME) // has to be here before we create antag otherwise we can't get the ckey of the demon + spawn_antag(chosen_one.client, get_turf(src), initial(demon_type.name), user.mind) to_chat(user, shatter_msg) to_chat(user, veil_msg) playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, TRUE) @@ -332,23 +329,22 @@ return to_chat(user, span_notice("You activate [src] and wait for confirmation.")) - var/list/baddie_candidates = SSpolling.poll_ghost_candidates( - "Do you want to play as a [role_to_play]?", + var/mob/chosen_one = SSpolling.poll_ghost_candidates( check_jobban = poll_role_check, role = poll_role_check, poll_time = 10 SECONDS, ignore_category = poll_ignore_category, - pic_source = src, + alert_pic = src, role_name_text = role_to_play, + amount_to_pick = 1 ) - if(!LAZYLEN(baddie_candidates)) + if(isnull(chosen_one)) to_chat(user, span_warning(fail_text)) return if(QDELETED(src) || !check_usability(user)) return used = TRUE - var/mob/dead/observer/ghostie = pick(baddie_candidates) - spawn_antag(ghostie.client, get_turf(src), user) + spawn_antag(chosen_one.client, get_turf(src), user) do_sparks(4, TRUE, src) qdel(src) diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index e56426a89ad11..401daa97ac5b2 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -286,7 +286,19 @@ GLOBAL_LIST_EMPTY(blob_nodes) blob_points = clamp(blob_points + points, 0, max_blob_points) hud_used.blobpwrdisplay.maptext = MAPTEXT("
[round(blob_points)]
") -/mob/camera/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/camera/blob/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if (!message) return diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index b35308d092f9d..2f3b51741f9b6 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -193,14 +193,20 @@ /mob/camera/blob/proc/pick_blobbernaut_candidate(obj/structure/blob/special/factory/factory) if(isnull(factory)) return - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), factory) - factory.AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_BLOB, \ - job_bans = ROLE_BLOB, \ - to_call = to_call, \ - title = "Blobbernaut", \ + var/icon/blobbernaut_icon = icon(icon, "blobbernaut") + blobbernaut_icon.Blend(blobstrain.color, ICON_MULTIPLY) + var/image/blobbernaut_image = image(blobbernaut_icon) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + check_jobban = ROLE_BLOB, + poll_time = 20 SECONDS, + checked_target = factory, + ignore_category = POLL_IGNORE_BLOB, + alert_pic = blobbernaut_image, + jump_target = factory, + role_name_text = "blobbernaut", + chat_text_border_icon = blobbernaut_image, ) + on_poll_concluded(factory, chosen_one) /// Called when the ghost poll concludes /mob/camera/blob/proc/on_poll_concluded(obj/structure/blob/special/factory/factory, mob/dead/observer/ghost) diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm index 9404157ad24ab..49d0ab4ad2300 100644 --- a/code/modules/antagonists/brother/brother.dm +++ b/code/modules/antagonists/brother/brother.dm @@ -77,22 +77,23 @@ flashed.balloon_alert(source, "[flashed.p_they()] resist!") return - flashed.mind.add_antag_datum(/datum/antagonist/brother, team) + if (!team.add_brother(flashed, key_name(source))) // Shouldn't happen given the former, more specific checks but just in case + flashed.balloon_alert(source, "failed!") + return + source.log_message("converted [key_name(flashed)] to blood brother", LOG_ATTACK) flashed.log_message("was converted by [key_name(source)] to blood brother", LOG_ATTACK) - log_game("[key_name(flashed)] converted [key_name(source)] to blood brother", list( - "flashed" = flashed, - "victim" = source, + log_game("[key_name(flashed)] was made into a blood brother by [key_name(source)]", list( + "converted" = flashed, + "converted by" = source, )) - - flashed.balloon_alert(source, "converted") - to_chat(source, span_notice("[span_bold("[flashed]")] has been converted to aide you as your Brother!")) flash.burn_out() flashed.mind.add_memory( \ /datum/memory/recruited_by_blood_brother, \ protagonist = flashed, \ antagonist = owner.current, \ ) + flashed.balloon_alert(source, "converted") UnregisterSignal(source, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON) source.RemoveComponentSource(REF(src), /datum/component/can_flash_from_behind) @@ -171,6 +172,34 @@ if (prob(10)) brothers_left += 1 +/datum/team/brother_team/add_member(datum/mind/new_member) + . = ..() + if (!new_member.has_antag_datum(/datum/antagonist/brother)) + add_brother(new_member.current) + +/datum/team/brother_team/remove_member(datum/mind/member) + if (!(member in members)) + return + . = ..() + member.remove_antag_datum(/datum/antagonist/brother) + if (isnull(member.current)) + return + for (var/datum/mind/brother_mind as anything in members) + to_chat(brother_mind, span_warning("[span_bold("[member.current.real_name]")] is no longer your brother!")) + update_name() + +/// Adds a new brother to the team +/datum/team/brother_team/proc/add_brother(mob/living/new_brother, source) + if (isnull(new_brother) || isnull(new_brother.mind) || !GET_CLIENT(new_brother) || new_brother.mind.has_antag_datum(/datum/antagonist/brother)) + return FALSE + + for (var/datum/mind/brother_mind as anything in members) + if (brother_mind == new_brother.mind) + continue + to_chat(brother_mind, span_notice("[span_bold("[new_brother.real_name]")] has been converted to aid you as your brother!")) + new_brother.mind.add_antag_datum(/datum/antagonist/brother, src) + return TRUE + /datum/team/brother_team/proc/update_name() var/list/last_names = list() for(var/datum/mind/team_minds as anything in members) diff --git a/code/modules/antagonists/changeling/powers/augmented_eyesight.dm b/code/modules/antagonists/changeling/powers/augmented_eyesight.dm index b4de878c69eed..dec2fa6a76e16 100644 --- a/code/modules/antagonists/changeling/powers/augmented_eyesight.dm +++ b/code/modules/antagonists/changeling/powers/augmented_eyesight.dm @@ -37,13 +37,13 @@ if(active) active = FALSE REMOVE_TRAIT(user, TRAIT_XRAY_VISION, REF(src)) - ling_eyes.flash_protect = FLASH_PROTECTION_WELDER + ling_eyes.flash_protect = max(ling_eyes.flash_protect += 3, FLASH_PROTECTION_WELDER) to_chat(user, span_changeling("We adjust our eyes to protect them from bright lights.")) else active = TRUE ADD_TRAIT(user, TRAIT_XRAY_VISION, REF(src)) - ling_eyes.flash_protect = FLASH_PROTECTION_SENSITIVE + ling_eyes.flash_protect = max(ling_eyes.flash_protect += -3, FLASH_PROTECTION_HYPER_SENSITIVE) to_chat(user, span_changeling("We adjust our eyes to sense prey through walls.")) user.update_sight() @@ -68,9 +68,9 @@ if(!istype(ling_eyes)) return if(active) - ling_eyes.flash_protect = FLASH_PROTECTION_SENSITIVE + ling_eyes.flash_protect = max(ling_eyes.flash_protect += -3, FLASH_PROTECTION_HYPER_SENSITIVE) else - ling_eyes.flash_protect = FLASH_PROTECTION_WELDER + ling_eyes.flash_protect = max(ling_eyes.flash_protect += 3, FLASH_PROTECTION_WELDER) /// Signal proc to remove flash sensitivity when the eyes are removed /datum/action/changeling/augmented_eyesight/proc/eye_removed(mob/living/source, obj/item/organ/removed, special) diff --git a/code/modules/antagonists/changeling/powers/chameleon_skin.dm b/code/modules/antagonists/changeling/powers/chameleon_skin.dm index 1924190cba36a..f74ad1208dd52 100644 --- a/code/modules/antagonists/changeling/powers/chameleon_skin.dm +++ b/code/modules/antagonists/changeling/powers/chameleon_skin.dm @@ -1,25 +1,25 @@ /datum/action/changeling/chameleon_skin name = "Chameleon Skin" - desc = "Our skin pigmentation rapidly changes to suit our current environment. Costs 25 chemicals." + desc = "Our skin pigmentation rapidly changes to suit our current environment. Costs 10 chemicals." helptext = "Allows us to become invisible after a few seconds of standing still. Can be toggled on and off." button_icon_state = "chameleon_skin" - dna_cost = 2 - chemical_cost = 25 + dna_cost = 1 + chemical_cost = 10 req_human = TRUE /datum/action/changeling/chameleon_skin/sting_action(mob/user) - var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = TRUE - if(!istype(H)) // req_human could be done in can_sting stuff. + var/mob/living/carbon/human/cling = user //SHOULD always be human, because req_human = TRUE + if(!istype(cling)) // req_human could be done in can_sting stuff. return ..() - if(H.dna.get_mutation(/datum/mutation/human/chameleon)) - H.dna.remove_mutation(/datum/mutation/human/chameleon) + if(cling.dna.get_mutation(/datum/mutation/human/chameleon/changeling)) + cling.dna.remove_mutation(/datum/mutation/human/chameleon/changeling) else - H.dna.add_mutation(/datum/mutation/human/chameleon) + cling.dna.add_mutation(/datum/mutation/human/chameleon/changeling) return TRUE /datum/action/changeling/chameleon_skin/Remove(mob/user) if(user.has_dna()) - var/mob/living/carbon/C = user - C.dna.remove_mutation(/datum/mutation/human/chameleon) + var/mob/living/carbon/cling = user + cling.dna.remove_mutation(/datum/mutation/human/chameleon/changeling) ..() diff --git a/code/modules/antagonists/changeling/powers/darkness_adaptation.dm b/code/modules/antagonists/changeling/powers/darkness_adaptation.dm new file mode 100644 index 0000000000000..c33b36a785f38 --- /dev/null +++ b/code/modules/antagonists/changeling/powers/darkness_adaptation.dm @@ -0,0 +1,83 @@ +/datum/action/changeling/darkness_adaptation + name = "Darkness Adaptation" + desc = "Our skin pigmentation and eyes rapidly changes to suit the darkness. Needs 10 chemicals in-storage to toggle. Slows down our chemical regeneration by 15%" + helptext = "Allows us to darken and change the translucency of our pigmentation, and adapt our eyes to see in dark conditions, \ + The translucent effect works best in dark enviroments and garments. Can be toggled on and off." + button_icon_state = "darkness_adaptation" + dna_cost = 2 + chemical_cost = 10 + + req_human = TRUE + //// is ability active (we are invisible)? + var/is_active = FALSE + /// How much we slow chemical regeneration while active, in chems per second + var/recharge_slowdown = 0.15 + +/datum/action/changeling/darkness_adaptation/on_purchase(mob/user, is_respec) + . = ..() + RegisterSignal(user, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(eye_implanted)) + RegisterSignal(user, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(eye_removed)) + +/datum/action/changeling/darkness_adaptation/sting_action(mob/living/carbon/human/cling) //SHOULD always be human, because req_human = TRUE + ..() + is_active = !is_active + if(is_active) + enable_ability(cling) + else + disable_ability(cling) + +/datum/action/changeling/darkness_adaptation/Remove(mob/living/carbon/human/cling) + ..() + disable_ability(cling) + +/datum/action/changeling/darkness_adaptation/proc/enable_ability(mob/living/carbon/human/cling) //Enable the adaptation + animate(cling, alpha = 65,time = 3 SECONDS) + cling.visible_message(span_warning("[cling]'s skin suddenly starts becoming translucent!"), \ + span_notice("We are now far more stealthy and better at seeing in the dark.")) + animate(cling, color = COLOR_DARK, time = 3 SECONDS) // Darkens their overall appearance + var/datum/antagonist/changeling/changeling_data = cling.mind?.has_antag_datum(/datum/antagonist/changeling) + changeling_data?.chem_recharge_slowdown -= recharge_slowdown //Slows down chem regeneration + var/obj/item/organ/internal/eyes/eyes = cling.get_organ_by_type(/obj/item/organ/internal/eyes) + if(!istype(eyes)) + return + eyes.lighting_cutoff = LIGHTING_CUTOFF_MEDIUM // Adds barely usable, kinda shit night vision + eyes.flash_protect = max(eyes.flash_protect += -1, FLASH_PROTECTION_HYPER_SENSITIVE) // Reduces flash protection by one level + cling.update_sight() // Update the display + +/datum/action/changeling/darkness_adaptation/proc/disable_ability(mob/living/carbon/human/cling) //Restore the adaptation + animate(cling, alpha = 255, time = 3 SECONDS) + cling.visible_message( + span_warning("[cling] appears from thin air!"), + span_notice("We are now appearing normal and lost the ability to see in the dark."), + ) + animate(cling, color = null, time = 3 SECONDS) + var/datum/antagonist/changeling/changeling_data = cling.mind?.has_antag_datum(/datum/antagonist/changeling) + changeling_data?.chem_recharge_slowdown += recharge_slowdown + var/obj/item/organ/internal/eyes/eyes = cling.get_organ_by_type(/obj/item/organ/internal/eyes) + if(!istype(eyes)) + return + eyes.lighting_cutoff = LIGHTING_CUTOFF_VISIBLE + eyes.flash_protect = max(eyes.flash_protect += 1, FLASH_PROTECTION_WELDER) + cling.update_sight() + +/// Signal proc to grant the correct level of flash sensitivity +/datum/action/changeling/darkness_adaptation/proc/eye_implanted(mob/living/source, obj/item/organ/gained, special) + SIGNAL_HANDLER + + var/obj/item/organ/internal/eyes/eyes = gained + if(!istype(eyes)) + return + if(is_active) + eyes.flash_protect = max(eyes.flash_protect += -1, FLASH_PROTECTION_HYPER_SENSITIVE) + else + eyes.flash_protect = max(eyes.flash_protect += 1, FLASH_PROTECTION_WELDER) + +/// Signal proc to remove flash sensitivity when the eyes are removed +/datum/action/changeling/darkness_adaptation/proc/eye_removed(mob/living/source, obj/item/organ/removed, special) + SIGNAL_HANDLER + + var/obj/item/organ/internal/eyes/eyes = removed + if(!istype(eyes)) + return + eyes.flash_protect = initial(eyes.flash_protect) + // We don't need to bother about removing or adding night vision, fortunately, because they can't see anyways diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm index bee8eec306f7c..01aac3e869161 100644 --- a/code/modules/antagonists/cult/cult_comms.dm +++ b/code/modules/antagonists/cult/cult_comms.dm @@ -147,17 +147,18 @@ asked_cultists += team_member.current var/list/yes_voters = SSpolling.poll_candidates( - question = "[nominee] seeks to lead your cult, do you support [nominee.p_them()]?", + question = "[span_notice(nominee)] seeks to lead your cult, do you support [nominee.p_them()]?", poll_time = 30 SECONDS, group = asked_cultists, - pic_source = nominee, - role_name_text = "cult master", + alert_pic = nominee, + role_name_text = "cult master nomination", custom_response_messages = list( POLL_RESPONSE_SIGNUP = "You have pledged your allegience to [nominee].", POLL_RESPONSE_ALREADY_SIGNED = "You have already pledged your allegience!", POLL_RESPONSE_NOT_SIGNED = "You aren't nominated for this.", POLL_RESPONSE_TOO_LATE_TO_UNREGISTER = "It's too late to unregister yourself, voting has already begun!", POLL_RESPONSE_UNREGISTERED = "You have been removed your pledge to [nominee].", + chat_text_border_icon = mutable_appearance('icons/effects/effects.dmi', "cult_master_logo") ) ) if(QDELETED(nominee) || nominee.incapacitated()) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index 8d7d66979f42b..8e5e7099be49a 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -737,13 +737,12 @@ GLOBAL_VAR_INIT(narsie_summon_count, 0) if(!mob_to_revive.client || mob_to_revive.client.is_afk()) set waitfor = FALSE - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [mob_to_revive.real_name], an inactive blood cultist?", check_jobban = ROLE_CULTIST, role = ROLE_CULTIST, poll_time = 5 SECONDS, target_mob = mob_to_revive, pic_source = mob_to_revive) - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger(mob_to_revive.real_name)], an [span_notice("inactive blood cultist")]?", check_jobban = ROLE_CULTIST, role = ROLE_CULTIST, poll_time = 5 SECONDS, checked_target = mob_to_revive, alert_pic = mob_to_revive, role_name_text = "inactive cultist") + if(chosen_one) to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.") - message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.") + message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.") mob_to_revive.ghostize(FALSE) - mob_to_revive.key = C.key + mob_to_revive.key = chosen_one.key else fail_invoke() return diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm index 18c957c28fd7d..acefd0e37173c 100644 --- a/code/modules/antagonists/disease/disease_mob.dm +++ b/code/modules/antagonists/disease/disease_mob.dm @@ -113,7 +113,19 @@ the new instance inside the host to be updated to the template's stats. for(var/datum/disease_ability/ability in purchased_abilities) . += span_notice("[ability.name]") -/mob/camera/disease/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/camera/disease/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!message) return if(sanitize) diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index f2cf7b0004771..bb59076a6bb06 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -537,23 +537,22 @@ animate(summoned, 10 SECONDS, alpha = 155) message_admins("A [summoned.name] is being summoned by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(summoned)].") - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [summoned.name]?", check_jobban = ROLE_HERETIC, poll_time = 10 SECONDS, target_mob = summoned, ignore_category = poll_ignore_define, pic_source = summoned, role_name_text = summoned.name) - if(!LAZYLEN(candidates)) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = ROLE_HERETIC, poll_time = 10 SECONDS, checked_target = summoned, ignore_category = poll_ignore_define, alert_pic = summoned, role_name_text = summoned.name) + if(isnull(chosen_one)) loc.balloon_alert(user, "ritual failed, no ghosts!") animate(summoned, 0.5 SECONDS, alpha = 0) QDEL_IN(summoned, 0.6 SECONDS) return FALSE - var/mob/dead/observer/picked_candidate = pick(candidates) // Ok let's make them an interactable mob now, since we got a ghost summoned.alpha = 255 REMOVE_TRAIT(summoned, TRAIT_NO_TRANSFORM, REF(src)) summoned.move_resist = initial(summoned.move_resist) summoned.ghostize(FALSE) - summoned.key = picked_candidate.key + summoned.key = chosen_one.key - user.log_message("created a [summoned.name], controlled by [key_name(picked_candidate)].", LOG_GAME) + 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)].") var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster) diff --git a/code/modules/antagonists/heretic/items/eldritch_painting.dm b/code/modules/antagonists/heretic/items/eldritch_painting.dm index 5aa63407dc6ef..5302fc1c9c148 100644 --- a/code/modules/antagonists/heretic/items/eldritch_painting.dm +++ b/code/modules/antagonists/heretic/items/eldritch_painting.dm @@ -92,7 +92,7 @@ if(!IS_HERETIC(examiner)) to_chat(examiner, span_hypnophrase("Respite, for now....")) examiner.mob_mood.mood_events.Remove("eldritch_weeping") - examiner.add_mood_event("weeping_withdrawl", /datum/mood_event/eldritch_painting/weeping_withdrawl) + examiner.add_mood_event("weeping_withdrawal", /datum/mood_event/eldritch_painting/weeping_withdrawal) return to_chat(examiner, span_notice("Oh, what arts! Just gazing upon it clears your mind.")) diff --git a/code/modules/antagonists/heretic/knowledge/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm index 3d4a9b3552db0..fe10f7949eae5 100644 --- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm @@ -42,7 +42,7 @@ /datum/heretic_knowledge/ashen_grasp name = "Grasp of Ash" - desc = "Your Mansus Grasp will burn the eyes of the victim, causing damage and blindness." + desc = "Your Mansus Grasp will burn the eyes of the victim, damaging them and blurring their vision." gain_text = "The Nightwatcher was the first of them, his treason started it all. \ Their lantern, expired to ash - their watch, absent." next_knowledge = list(/datum/heretic_knowledge/spell/ash_passage) @@ -70,7 +70,7 @@ /datum/heretic_knowledge/spell/ash_passage name = "Ashen Passage" - desc = "Grants you Ashen Passage, a silent but short range jaunt." + desc = "Grants you Ashen Passage, a spell that lets you phase out of reality and traverse a short distance, passing though any walls." gain_text = "He knew how to walk between the planes." next_knowledge = list( /datum/heretic_knowledge/mark/ash_mark, @@ -181,6 +181,7 @@ 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. \ You will also become immune to flames, space, and similar environmental hazards." 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, \ diff --git a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm index 1a16f2e9f9321..09efb5c2eb8f4 100644 --- a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm @@ -42,7 +42,8 @@ /datum/heretic_knowledge/cosmic_grasp name = "Grasp of Cosmos" - desc = "Your Mansus Grasp will give people a star mark (cosmic ring) and create a cosmic field where you stand." + desc = "Your Mansus Grasp will give people a star mark (cosmic ring) and create a cosmic field where you stand. \ + People with a star mark can not pass cosmic fields." gain_text = "Some stars dimmed, others' magnitude increased. \ With newfound strength I could channel the nebula's power into myself." next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_runes) @@ -82,7 +83,8 @@ /datum/heretic_knowledge/mark/cosmic_mark name = "Mark of Cosmos" desc = "Your Mansus Grasp now applies the Mark of Cosmos. The mark is triggered from an attack with your Cosmic Blade. \ - When triggered, the victim is returned to the location where the mark was originally applied to them. \ + When triggered, the victim is returned to the location where the mark was originally applied to them, \ + leaving a cosmic field in their place. \ They will then be paralyzed for 2 seconds." gain_text = "The Beast now whispered to me occasionally, only small tidbits of their circumstances. \ I can help them, I have to help them." @@ -98,8 +100,7 @@ name = "Star Touch" desc = "Grants you Star Touch, a spell which places a star mark upon your target \ and creates a cosmic field at your feet and to the turfs next to you. Targets which already have a star mark \ - will be forced to sleep for 4 seconds. When the victim is hit it also creates a beam that \ - deals a bit of fire damage and damages the cells. \ + will be forced to sleep for 4 seconds. When the victim is hit it also creates a beam that burns them. \ The beam lasts a minute, until the beam is obstructed or until a new target has been found." gain_text = "After waking in a cold sweat I felt a palm on my scalp, a sigil burned onto me. \ My veins now emitted a strange purple glow, the Beast knows I will surpass its expectations." @@ -110,7 +111,7 @@ /datum/heretic_knowledge/spell/star_blast name = "Star Blast" - desc = "Fires a projectile that moves very slowly and creates cosmic fields on impact. \ + desc = "Fires a projectile that moves very slowly, raising a short-lived wall of cosmic fields where it goes. \ Anyone hit by the projectile will receive burn damage, a knockdown, and give people in a three tile range a star mark." gain_text = "The Beast was behind me now at all times, with each sacrifice words of affirmation coursed through me." next_knowledge = list( @@ -243,7 +244,8 @@ 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." + 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." 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. \ diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm index f41512d76c069..8898ba7f59c66 100644 --- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm @@ -122,6 +122,7 @@ /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. \ Voiceless Dead are mute ghouls and only have 50 health, but can use Bloody Blades effectively. \ You can only create two at a time." gain_text = "I found notes of a dark ritual, unfinished... yet still, I pushed forward." @@ -167,15 +168,13 @@ if(!soon_to_be_ghoul.mind || !soon_to_be_ghoul.client) message_admins("[ADMIN_LOOKUPFLW(user)] is creating a voiceless dead of a body with no player.") - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a [soon_to_be_ghoul.real_name], a voiceless dead?", check_jobban = ROLE_HERETIC, role = ROLE_HERETIC, poll_time = 5 SECONDS, target_mob = soon_to_be_ghoul, pic_source = soon_to_be_ghoul, role_name_text = "voiceless dead") - if(!LAZYLEN(candidates)) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger(soon_to_be_ghoul.real_name)], a [span_notice("voiceless dead")]?", check_jobban = ROLE_HERETIC, role = ROLE_HERETIC, poll_time = 5 SECONDS, checked_target = soon_to_be_ghoul, alert_pic = mutable_appearance('icons/mob/human/human.dmi', "husk"), jump_target = soon_to_be_ghoul, role_name_text = "voiceless dead") + if(isnull(chosen_one)) loc.balloon_alert(user, "ritual failed, no ghosts!") return FALSE - - var/mob/dead/observer/chosen_candidate = pick(candidates) - message_admins("[key_name_admin(chosen_candidate)] has taken control of ([key_name_admin(soon_to_be_ghoul)]) to replace an AFK player.") + message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(soon_to_be_ghoul)]) to replace an AFK player.") soon_to_be_ghoul.ghostize(FALSE) - soon_to_be_ghoul.key = chosen_candidate.key + soon_to_be_ghoul.key = chosen_one.key selected_atoms -= soon_to_be_ghoul make_ghoul(user, soon_to_be_ghoul) diff --git a/code/modules/antagonists/heretic/knowledge/lock_lore.dm b/code/modules/antagonists/heretic/knowledge/lock_lore.dm index 9f80f47b0ae48..0727b86bb668e 100644 --- a/code/modules/antagonists/heretic/knowledge/lock_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/lock_lore.dm @@ -92,9 +92,12 @@ /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. \ - It functions the same as an ID Card, but attacking it with an ID card fuses it and gains its access. \ - You can use it in-hand to change its form to a card you fused. \ - Does not preserve the card used in the ritual." + 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. \ + 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. \ + Does not preserve the card originally used in the ritual." 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, @@ -186,7 +189,8 @@ 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 in addition, create a tear to the Labyrinth's heart; \ + 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." diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm index d7d1bd3bf22a7..723599ad262f5 100644 --- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm @@ -45,8 +45,8 @@ /datum/heretic_knowledge/moon_grasp name = "Grasp of Lunacy" - desc = "Your Mansus Grasp will cause them to hallucinate everyone as lunar mass, \ - and hides your identity for a short dur ation." + desc = "Your Mansus Grasp will cause your victims to hallucinate everyone as lunar mass, \ + and hides your identity for a short duration." gain_text = "The troupe on the side of the moon showed me truth, and I took it." next_knowledge = list(/datum/heretic_knowledge/spell/moon_smile) cost = 1 @@ -85,9 +85,8 @@ /datum/heretic_knowledge/mark/moon_mark name = "Mark of Moon" - desc = "Your Mansus Grasp now applies the Mark of Moon. The mark is triggered from an attack with your Moon Blade. \ - When triggered, the victim is confused, and when the mark is applied they are pacified \ - until attacked." + desc = "Your Mansus Grasp now applies the Mark of Moon, pacifying the victim until attacked. \ + The mark can also be triggered from an attack with your Moon Blade, leaving the victim confused." gain_text = "The troupe on the moon would dance all day long \ and in that dance the moon would smile upon us \ but when the night came its smile would dull forced to gaze on the earth." @@ -112,9 +111,9 @@ /datum/heretic_knowledge/moon_amulette name = "Moonlight Amulette" - desc = "Allows you to transmute 2 sheets of glass, a heart and a tie \ - if the item is used on someone with low sanity they go berserk attacking everyone \ - , if their sanity isnt low enough it decreases their mood." + desc = "Allows you to transmute 2 sheets of glass, a heart and a tie to create a Moonlight Amulette. \ + 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." gain_text = "At the head of the parade he stood, the moon condensed into one mass, a reflection of the soul." next_knowledge = list( /datum/heretic_knowledge/blade_upgrade/moon, @@ -153,9 +152,9 @@ /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,\ - causes hallucinations with those who have less sanity getting more. \ - If their sanity is low enough turns them insane, the spell then halves their sanity." + 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." 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." next_knowledge = list( @@ -170,8 +169,8 @@ 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. \ - When completed, you become a harbinger of madness gaining and aura of passive sanity decrease \ - , confusion increase and if their sanity is low enough brain damage and blindness. \ + When completed, you become a harbinger of madness gaining and aura of passive sanity decrease, \ + confusion increase and, if their sanity is low enough, brain damage and blindness. \ 1/5th of the crew will turn into acolytes and follow your command, they will all recieve moonlight amulettes." 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 \ diff --git a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm index 4ce8f9de9c936..a4810c706c118 100644 --- a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm +++ b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm @@ -21,7 +21,7 @@ name = "Curse of Paralysis" desc = "Allows you to transmute a hatchet and both a left and right leg to cast a curse of immobility on a crew member. \ While cursed, the victim will be unable to walk. You can additionally supply an item that a victim has touched \ - or is covered in the victim's blood to empower the curse." + or is covered in the victim's blood to make the curse last longer." gain_text = "The flesh of humanity is weak. Make them bleed. Show them their fragility." next_knowledge = list( /datum/heretic_knowledge/mad_mask, @@ -58,8 +58,8 @@ /datum/heretic_knowledge/summon/ashy name = "Ashen Ritual" - desc = "Allows you to transmute a head, a pile of ash, and a book to create an Ash Man. \ - Ash Men have a short range jaunt and the ability to cause bleeding in foes at range. \ + desc = "Allows you to transmute a head, a pile of ash, and a book to create 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." gain_text = "I combined my principle of hunger with my desire for destruction. The Marshal knew my name, and the Nightwatcher gazed on." next_knowledge = list( diff --git a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm index 470d98e178b7e..14a003ce11c0b 100644 --- a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm +++ b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm @@ -36,8 +36,9 @@ /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 bolt or unbolt nearby doors \ - when landing on tails. If the coin gets inserted into an airlock it emags the door destroying the 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. " gain_text = "The Mansus is a place of all sorts of sins. But greed held a special role." next_knowledge = list( /datum/heretic_knowledge/spell/cosmic_expansion, diff --git a/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm b/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm index e2825c6db2869..74013f2b0bd1d 100644 --- a/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm +++ b/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm @@ -2,7 +2,8 @@ /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. \ - It removes your restraints, repels and knocks down adjacent people, and applies the Mansus Grasp to everything nearby." + It removes your restraints, repels and knocks down adjacent people, and applies the Mansus Grasp to everything nearby. \ + However, you will fall unconscious a short time after casting this spell." gain_text = "My shackles undone in dark fury, their feeble bindings crumble before my power." next_knowledge = list( /datum/heretic_knowledge/summon/raw_prophet, diff --git a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm index 737e0f08f40a1..f1dd564310be5 100644 --- a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm +++ b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm @@ -16,10 +16,10 @@ /datum/heretic_knowledge/unfathomable_curio name = "Unfathomable Curio" - desc = "Allows you to transmute 3 rods, a brain and a belt into an Unfathomable Curio\ - , a belt that can hold blades and items for rituals. Whilst worn will also \ + 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 also \ veil you, allowing you to take 5 hits without suffering damage, this veil will recharge very slowly \ - outside of combat. When examined the examiner will suffer brain damage and blindness." + outside of combat." gain_text = "The mansus holds many a curio, some are not meant for the mortal eye." next_knowledge = list( /datum/heretic_knowledge/spell/burglar_finesse, @@ -38,12 +38,12 @@ name = "Unsealed Arts" desc = "Allows you to transmute a canvas and an additional item to create a piece of art, these paintings \ have different effects depending on the additional item added. Possible paintings: \ - The sister and He Who Wept: Eyes. When a non-heretic looks at the painting they will begin to hallucinate everyone as heretics. \ - The First Desire: Any bodypart. Increases the hunger of non-heretics, when examined drops an organ or body part at your feet. \ - Great chaparral over rolling hills: Any grown food. Spreads kudzu when placed, when examined grants a flower. \ - Lady out of gates: Gloves. Causes non-heretics to scratch themselves, when examined removes all your mutations. \ - Climb over the rusted mountain: Trash. Causes non-heretics to rust the floor they walk on. \ - These effects are mitigated for a few minutes when a non-heretic suffering an effect examines the painting that caused the effect." + The sister and He Who Wept: Eyes. Clears your own mind, but curses non-heretics with hallucinations. \ + The First Desire: Any bodypart. Supplies you with random organs, but curses non-heretics with a hunger for flesh. \ + Great chaparral over rolling hills: Any grown food. Spreads kudzu when placed and examined by non-heretics. Also supplies you with poppies and harebells. \ + Lady out of gates: Gloves. Clears your mutations, but mutates non-heretics and curses them with scratching. \ + Climb over the rusted mountain: Trash. Curses non-heretics to rust the floor they walk on. \ + Non-heretics can counter most of these effects by examining one of these paintings." gain_text = "A wind of inspiration blows through me, past the walls and past the gate inspirations lie, yet to be depicted. \ They yearn for mortal eyes again, and I shall grant that wish." next_knowledge = list( diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm index 2dbb44ea4eb7e..3d326b4a9af45 100644 --- a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm +++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm @@ -44,7 +44,7 @@ name = "Curse of Corrosion" desc = "Allows you to transmute wirecutters, a pool of vomit, and a heart to cast a curse of sickness on a crew member. \ While cursed, the victim will repeatedly vomit while their organs will take constant damage. You can additionally supply an item \ - that a victim has touched or is covered in the victim's blood to empower the curse." + that a victim has touched or is covered in the victim's blood to make the curse last longer." gain_text = "The body of humanity is temporary. Their weaknesses cannot be stopped, like iron falling to rust. Show them all." next_knowledge = list( /datum/heretic_knowledge/spell/area_conversion, diff --git a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm index 643fd434af7b5..e044eee8619ef 100644 --- a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm +++ b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm @@ -144,7 +144,8 @@ name = "Maid in the Mirror" desc = "Allows you to transmute five sheets of titanium, a flash, a suit of armor, 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." + phasing in and out of the mirror realm, serving as powerful scouts and ambushers. \ + However, they are weak to mortal gaze and take damage by being examined." 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." next_knowledge = list( diff --git a/code/modules/antagonists/heretic/magic/apetravulnera.dm b/code/modules/antagonists/heretic/magic/apetravulnera.dm index 801104dddf9fc..e80d08911848c 100644 --- a/code/modules/antagonists/heretic/magic/apetravulnera.dm +++ b/code/modules/antagonists/heretic/magic/apetravulnera.dm @@ -5,7 +5,7 @@ background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" button_icon = 'icons/mob/actions/actions_ecult.dmi' - button_icon_state = "cleave" + button_icon_state = "apetra_vulnera" school = SCHOOL_FORBIDDEN cooldown_time = 45 SECONDS @@ -23,7 +23,7 @@ /datum/action/cooldown/spell/pointed/apetra_vulnera/cast(mob/living/carbon/human/cast_on) . = ..() - + if(IS_HERETIC_OR_MONSTER(cast_on)) return FALSE @@ -44,7 +44,7 @@ a_limb_got_damaged = TRUE var/datum/wound/slash/crit_wound = new wound_type() crit_wound.apply_wound(bodypart) - + if(!a_limb_got_damaged) var/datum/wound/slash/crit_wound = new wound_type() crit_wound.apply_wound(pick(cast_on.bodyparts)) @@ -53,7 +53,7 @@ span_danger("[cast_on]'s scratches and bruises are torn open by an unholy force!"), span_danger("Your scratches and bruises are torn open by some horrible unholy force!") ) - + new /obj/effect/temp_visual/cleave(get_turf(cast_on)) return TRUE diff --git a/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm b/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm index 18e8d26fecc60..e792dc116da6f 100644 --- a/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm +++ b/code/modules/antagonists/heretic/magic/ascended_shapeshift.dm @@ -6,6 +6,8 @@ cooldown_time = 20 SECONDS convert_damage = FALSE die_with_shapeshifted_form = FALSE + button_icon = 'icons/mob/actions/actions_ecult.dmi' + button_icon_state = "lock_ascension" possible_shapes = list( /mob/living/basic/heretic_summon/ash_spirit, /mob/living/basic/heretic_summon/raw_prophet/ascended, diff --git a/code/modules/antagonists/heretic/magic/caretaker.dm b/code/modules/antagonists/heretic/magic/caretaker.dm index 29fcecf076fb0..86ff285001917 100644 --- a/code/modules/antagonists/heretic/magic/caretaker.dm +++ b/code/modules/antagonists/heretic/magic/caretaker.dm @@ -6,8 +6,8 @@ and you can be removed from it upon contact with antimagical artifacts." background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" - button_icon = 'icons/mob/actions/actions_minor_antag.dmi' - button_icon_state = "ninja_cloak" + button_icon = 'icons/mob/actions/actions_ecult.dmi' + button_icon_state = "caretaker" sound = 'sound/effects/curse2.ogg' school = SCHOOL_FORBIDDEN diff --git a/code/modules/antagonists/heretic/magic/cosmic_runes.dm b/code/modules/antagonists/heretic/magic/cosmic_runes.dm index 5115a2181fa91..4af3b94b44f34 100644 --- a/code/modules/antagonists/heretic/magic/cosmic_runes.dm +++ b/code/modules/antagonists/heretic/magic/cosmic_runes.dm @@ -1,6 +1,7 @@ /datum/action/cooldown/spell/cosmic_rune name = "Cosmic Rune" - desc = "Creates a cosmic rune at your position, only two can exist at a time. Invoking one rune transports you to the other." + desc = "Creates a cosmic rune at your position, only two can exist at a time. Invoking one rune transports you to the other. \ + Anyone with a star mark gets transported along with you." background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" button_icon = 'icons/mob/actions/actions_ecult.dmi' diff --git a/code/modules/antagonists/heretic/magic/moon_parade.dm b/code/modules/antagonists/heretic/magic/moon_parade.dm index 409e55bf9261a..3b7f1d007cd6e 100644 --- a/code/modules/antagonists/heretic/magic/moon_parade.dm +++ b/code/modules/antagonists/heretic/magic/moon_parade.dm @@ -1,6 +1,6 @@ /datum/action/cooldown/spell/pointed/projectile/moon_parade name = "Lunar parade" - desc = "This unleashes the parade towards a target." + desc = "This unleashes the parade, making everyone in its way join it and suffer hallucinations." background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" button_icon = 'icons/mob/actions/actions_ecult.dmi' diff --git a/code/modules/antagonists/heretic/magic/moon_ringleader.dm b/code/modules/antagonists/heretic/magic/moon_ringleader.dm index 45d3285a876da..3c0b1d2aedb52 100644 --- a/code/modules/antagonists/heretic/magic/moon_ringleader.dm +++ b/code/modules/antagonists/heretic/magic/moon_ringleader.dm @@ -1,7 +1,8 @@ /datum/action/cooldown/spell/aoe/moon_ringleader name = "Ringleaders Rise" - desc = "Big AoE spell that deals more brain damage the lower the sanity of everyone in the AoE and it also causes hallucinations with those who have less sanity getting more. \ - If their sanity is low enough they snap and go insane, the spell then halves their sanity." + desc = "Big AoE spell that deals brain damage and causes hallucinations to everyone in the AoE. \ + The worse their sanity, the stronger this spell becomes. \ + If their sanity is low enough, they even snap and go insane, and the spell then further halves their sanity." background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" button_icon = 'icons/mob/actions/actions_ecult.dmi' diff --git a/code/modules/antagonists/heretic/magic/moon_smile.dm b/code/modules/antagonists/heretic/magic/moon_smile.dm index 893059721c428..90a392691e9fa 100644 --- a/code/modules/antagonists/heretic/magic/moon_smile.dm +++ b/code/modules/antagonists/heretic/magic/moon_smile.dm @@ -28,7 +28,8 @@ /datum/action/cooldown/spell/pointed/moon_smile/cast(mob/living/carbon/human/cast_on) . = ..() /// The duration of these effects are based on sanity, mainly for flavor but also to make it a weaker alpha strike - var/moon_smile_duration = (150 - cast_on.mob_mood.sanity) / 10 + var/maximum_duration = 15 SECONDS + var/moon_smile_duration = ((SANITY_MAXIMUM - cast_on.mob_mood.sanity) / (SANITY_MAXIMUM - SANITY_INSANE)) * maximum_duration if(cast_on.can_block_magic(antimagic_flags)) to_chat(cast_on, span_notice("The moon turns, its smile no longer set on you.")) to_chat(owner, span_warning("The moon does not smile upon them.")) @@ -40,7 +41,8 @@ cast_on.set_eye_blur_if_lower(moon_smile_duration + 7 SECONDS) var/obj/item/organ/internal/ears/ears = cast_on.get_organ_slot(ORGAN_SLOT_EARS) - ears?.adjustEarDamage(0, moon_smile_duration + 2 SECONDS) + //adjustEarDamage takes deafness duration parameter in one unit per two seconds, instead of the normal time, so we divide by two seconds + ears?.adjustEarDamage(0, (moon_smile_duration + 2 SECONDS) / (2 SECONDS)) cast_on.adjust_silence(moon_smile_duration + 5 SECONDS) cast_on.AdjustKnockdown(2 SECONDS) diff --git a/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm b/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm index 64638d7103b17..4e37f5db17fed 100644 --- a/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm +++ b/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm @@ -1,6 +1,6 @@ /datum/action/cooldown/spell/aoe/fiery_rebirth name = "Nightwatcher's Rebirth" - desc = "A spell that extinguishes you drains nearby heathens engulfed in flames of their life force, \ + desc = "A spell that extinguishes you and drains nearby heathens engulfed in flames of their life force, \ healing you for each victim drained. Those in critical condition \ will have the last of their vitality drained, killing them." background_icon_state = "bg_heretic" diff --git a/code/modules/antagonists/heretic/magic/rust_charge.dm b/code/modules/antagonists/heretic/magic/rust_charge.dm index d5427cf376262..56054bd56fdd8 100644 --- a/code/modules/antagonists/heretic/magic/rust_charge.dm +++ b/code/modules/antagonists/heretic/magic/rust_charge.dm @@ -1,7 +1,9 @@ // Rust charge, a charge action that can only be started on rust (and only destroys rust tiles) /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 empoweres you for this ability, no focus is needed" + 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." charge_distance = 10 charge_damage = 50 cooldown_time = 45 SECONDS diff --git a/code/modules/antagonists/heretic/magic/star_blast.dm b/code/modules/antagonists/heretic/magic/star_blast.dm index 212e90535d6c7..48fdf2f26934b 100644 --- a/code/modules/antagonists/heretic/magic/star_blast.dm +++ b/code/modules/antagonists/heretic/magic/star_blast.dm @@ -1,6 +1,6 @@ /datum/action/cooldown/spell/pointed/projectile/star_blast name = "Star Blast" - desc = "This spell fires a disk with cosmic energies at a target." + desc = "This spell fires a disk with cosmic energies at a target, spreading the star mark." background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" button_icon = 'icons/mob/actions/actions_ecult.dmi' diff --git a/code/modules/antagonists/heretic/magic/star_touch.dm b/code/modules/antagonists/heretic/magic/star_touch.dm index 9037d07295a94..89c5d02e7d498 100644 --- a/code/modules/antagonists/heretic/magic/star_touch.dm +++ b/code/modules/antagonists/heretic/magic/star_touch.dm @@ -1,7 +1,8 @@ /datum/action/cooldown/spell/touch/star_touch name = "Star Touch" - desc = "Marks someone with a star mark or puts someone with a star mark to sleep for 4 seconds, removing the star mark. \ - You and your target are linked with a cosmic ray, burning them for up to a minute, or \ + desc = "Manifests cosmic fields on tiles next to you while marking the victim with a star mark \ + or consuming an already present star mark to put them to sleep for 4 seconds. \ + They will then be linked to you with a cosmic ray, burning them for up to a minute, or \ until they can escape your sight. Star Touch can also remove Cosmic Runes, or teleport you \ to your Star Gazer when used on yourself." background_icon_state = "bg_heretic" diff --git a/code/modules/antagonists/heretic/status_effects/debuffs.dm b/code/modules/antagonists/heretic/status_effects/debuffs.dm index 08839fa8f1058..7037d1cc3778b 100644 --- a/code/modules/antagonists/heretic/status_effects/debuffs.dm +++ b/code/modules/antagonists/heretic/status_effects/debuffs.dm @@ -280,7 +280,7 @@ /datum/status_effect/moon_converted/on_remove() // Span warning and unconscious so they realize they aren't evil anymore - to_chat(owner, span_warning("Your mind is cleared from the effect of the manus, your alligiences are as they were before")) + to_chat(owner, span_warning("Your mind is cleared from the effect of the mansus, your alligiences are as they were before")) REMOVE_TRAIT(owner, TRAIT_MUTE, REF(src)) owner.AdjustUnconscious(5 SECONDS, ignore_canstun = FALSE) owner.log_message("[owner] is no longer insane.", LOG_GAME) diff --git a/code/modules/antagonists/heretic/structures/lock_final.dm b/code/modules/antagonists/heretic/structures/lock_final.dm index 8cb6c06f3cb01..759bc8aa55e39 100644 --- a/code/modules/antagonists/heretic/structures/lock_final.dm +++ b/code/modules/antagonists/heretic/structures/lock_final.dm @@ -37,7 +37,7 @@ /// Ask ghosts if they want to make some noise /obj/structure/lock_tear/proc/poll_ghosts() - var/list/candidates = SSpolling.poll_ghost_candidates("Would you like to be a random eldritch monster attacking the crew?", check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_HERETIC_MONSTER, pic_source = src, role_name_text = "eldritch monster") + var/list/candidates = SSpolling.poll_ghost_candidates("Would you like to be a random [span_notice("eldritch monster")] attacking the crew?", check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_HERETIC_MONSTER, alert_pic = src, role_name_text = "eldritch monster") while(LAZYLEN(candidates)) var/mob/dead/observer/candidate = pick_n_take(candidates) ghost_to_monster(candidate, should_ask = FALSE) diff --git a/code/modules/antagonists/nightmare/nightmare_species.dm b/code/modules/antagonists/nightmare/nightmare_species.dm index 068bb2b6c5c1f..38db2dfae8657 100644 --- a/code/modules/antagonists/nightmare/nightmare_species.dm +++ b/code/modules/antagonists/nightmare/nightmare_species.dm @@ -39,7 +39,6 @@ . = ..() C.fully_replace_character_name(null, pick(GLOB.nightmare_names)) - C.set_safe_hunger_level() /datum/species/shadow/nightmare/check_roundstart_eligible() return FALSE diff --git a/code/modules/antagonists/nukeop/datums/operative_team.dm b/code/modules/antagonists/nukeop/datums/operative_team.dm index e42d65b42a845..9bec3b0fcf0e1 100644 --- a/code/modules/antagonists/nukeop/datums/operative_team.dm +++ b/code/modules/antagonists/nukeop/datums/operative_team.dm @@ -154,19 +154,17 @@ var/tc_to_spawn = tgui_input_number(admin, "How much TC to spawn with?", "TC", 0, 100) - var/list/nuke_candidates = SSpolling.poll_ghost_candidates( - "Do you want to play as an emergency syndicate reinforcement?", + var/mob/chosen_one = SSpolling.poll_ghost_candidates( check_jobban = ROLE_OPERATIVE, role = ROLE_OPERATIVE, poll_time = 30 SECONDS, ignore_category = POLL_IGNORE_SYNDICATE, - pic_source = /obj/structure/sign/poster/contraband/gorlex_recruitment, - role_name_text = "syndicate reinforcement", + alert_pic = /obj/structure/sign/poster/contraband/gorlex_recruitment, + role_name_text = "emergency syndicate reinforcement", + amount_to_pick = 1, ) - nuke_candidates -= admin // may be easy to fat-finger say yes. so just don't - - if(!length(nuke_candidates)) + if(isnull(chosen_one)) tgui_alert(admin, "No candidates found.", "Recruitment Shortage", list("OK")) return @@ -194,10 +192,9 @@ if(infil_or_nukebase == SPAWN_AT_BASE) spawn_loc = pick(GLOB.nukeop_start) - var/mob/dead/observer/picked = pick(nuke_candidates) var/mob/living/carbon/human/nukie = new(spawn_loc) - picked.client.prefs.safe_transfer_prefs_to(nukie, is_antag = TRUE) - nukie.key = picked.key + chosen_one.client.prefs.safe_transfer_prefs_to(nukie, is_antag = TRUE) + nukie.key = chosen_one.key var/datum/antagonist/nukeop/antag_datum = new() antag_datum.send_to_spawnpoint = FALSE diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm b/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm index 72c51f14b2b99..c318679b4f6fe 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm @@ -26,7 +26,7 @@ /obj/item/disk/nuclear/Initialize(mapload) . = ..() - AddElement(/datum/element/bed_tuckable, 6, -6, 0) + AddElement(/datum/element/bed_tuckable, mapload, 6, -6, 0) AddComponent(/datum/component/stationloving, !fake) if(!fake) diff --git a/code/modules/antagonists/nukeop/outfits.dm b/code/modules/antagonists/nukeop/outfits.dm index e9a293c3e9981..80360f5636098 100644 --- a/code/modules/antagonists/nukeop/outfits.dm +++ b/code/modules/antagonists/nukeop/outfits.dm @@ -77,6 +77,7 @@ backpack_contents = list( /obj/item/gun/ballistic/automatic/pistol/clandestine = 1, /obj/item/pen/edagger = 1, + /obj/item/ammo_box/magazine/m12g = 3, ) /datum/outfit/syndicate/full/plasmaman diff --git a/code/modules/antagonists/pirate/pirate_event.dm b/code/modules/antagonists/pirate/pirate_event.dm index f3c6655a27572..e4a14182d0e7f 100644 --- a/code/modules/antagonists/pirate/pirate_event.dm +++ b/code/modules/antagonists/pirate/pirate_event.dm @@ -66,7 +66,7 @@ if(chosen_gang.paid_off) return - var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a pirate crew of [chosen_gang.name]?", check_jobban = ROLE_TRAITOR, pic_source = /obj/item/claymore/cutlass, role_name_text = "pirate crew") + var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a [span_notice("pirate crew of [chosen_gang.name]?")]", check_jobban = ROLE_TRAITOR, alert_pic = /obj/item/claymore/cutlass, role_name_text = "pirate crew") shuffle_inplace(candidates) var/template_key = "pirate_[chosen_gang.ship_template_id]" diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm index 035ebba340512..e3c96fc815f3b 100644 --- a/code/modules/antagonists/spy/spy_bounty.dm +++ b/code/modules/antagonists/spy/spy_bounty.dm @@ -141,7 +141,15 @@ if(isitem(stealing) && ((stealing.resistance_flags & INDESTRUCTIBLE) || prob(black_market_prob))) addtimer(CALLBACK(src, PROC_REF(send_to_black_market), stealing), 0.5 SECONDS) else - QDEL_IN(stealing, 0.5 SECONDS) + addtimer(CALLBACK(src, PROC_REF(finish_cleanup), stealing), 0.5 SECONDS) + +/** + * Called when cleaning up a stolen atom that was NOT sent to the black market. + * + * * stealing - The item that was stolen. + */ +/datum/spy_bounty/proc/finish_cleanup(atom/movable/stealing) + qdel(stealing) /** * Handles putting the passed movable up on the black market. @@ -311,6 +319,10 @@ return TRUE +/datum/spy_bounty/machine/finish_cleanup(obj/machinery/stealing) + stealing.dump_inventory_contents() + return ..() + /datum/spy_bounty/machine/init_bounty(datum/spy_bounty_handler/handler) if(isnull(target_type)) return FALSE @@ -438,7 +450,7 @@ /datum/spy_bounty/machine/random/hard/ai_sat_teleporter random_options = list( /obj/machinery/teleport, - /obj/machinery/computer/teleporter. + /obj/machinery/computer/teleporter, ) location_type = /area/station/ai_monitored/aisat @@ -626,6 +638,13 @@ /datum/spy_bounty/some_bot/get_dupe_protection_key(atom/movable/stealing) return bot_type +/datum/spy_bounty/some_bot/finish_cleanup(mob/living/simple_animal/bot/stealing) + if(stealing.client) + to_chat(stealing, span_deadsay("You've been stolen! You are shipped off to the black market and taken apart for spare parts...")) + stealing.investigate_log("stole by a spy (and deleted)", INVESTIGATE_DEATHS) + stealing.ghostize() + return ..() + /datum/spy_bounty/some_bot/init_bounty(datum/spy_bounty_handler/handler) for(var/datum/spy_bounty/some_bot/existing_bounty in handler.get_all_bounties()) var/mob/living/simple_animal/bot/existing_bot_type = existing_bounty.bot_type diff --git a/code/modules/antagonists/spy/spy_uplink.dm b/code/modules/antagonists/spy/spy_uplink.dm index ea6f39fc92d4b..2a9d9b9b14e9b 100644 --- a/code/modules/antagonists/spy/spy_uplink.dm +++ b/code/modules/antagonists/spy/spy_uplink.dm @@ -97,6 +97,7 @@ to_chat(spy, span_warning("Your uplinks blinks red: [stealing] cannot be extracted from there.")) return FALSE + log_combat(spy, stealing, "started stealing", parent, "(spy bounty)") playsound(stealing, 'sound/items/pshoom.ogg', 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.33, ignore_walls = FALSE) var/obj/effect/scan_effect/active_scan_effect = new(stealing.loc) @@ -160,7 +161,8 @@ playsound(parent, 'sound/machines/wewewew.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - log_spy("[key_name(spy)] completed the bounty [bounty.name] of difficulty [bounty.difficulty] for \a [reward].") + log_combat(spy, stealing, "stole", parent, "(spy bounty)") + log_spy("[key_name(spy)] completed the bounty [bounty.name] of difficulty [bounty.difficulty] by stealing [stealing] for \a [reward].") SSblackbox.record_feedback("nested tally", "spy_bounty", 1, list("[stealing.type]", "[bounty.type]", "[bounty.difficulty]", "[bounty.reward_item.type]")) var/datum/antagonist/spy/spy_datum = spy_ref?.resolve() diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 02357d22e8b41..78cd4f4929c8a 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -312,15 +312,17 @@ return TRUE to_chat(user, "[span_userdanger("Capture failed!")]: The soul has already fled its mortal frame. You attempt to bring it back...") - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user, victim) - AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_SHADE, \ - job_bans = ROLE_CULTIST, \ - to_call = to_call, \ - title = "A shade" \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + check_jobban = ROLE_CULTIST, + poll_time = 20 SECONDS, + checked_target = src, + ignore_category = POLL_IGNORE_SHADE, + alert_pic = /mob/living/basic/shade, + jump_target = src, + role_name_text = "a shade", + chat_text_border_icon = /mob/living/basic/shade, ) - + on_poll_concluded(user, victim, chosen_one) return TRUE //it'll probably get someone ;) ///captures a shade that was previously released from a soulstone. diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm index 92489145fda97..85267c0333c45 100644 --- a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm +++ b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm @@ -148,6 +148,7 @@ color = COLOR_PALE_GREEN light_range = 2 light_color = COLOR_PALE_GREEN + resistance_flags = parent_type::resistance_flags | SHUTTLE_CRUSH_PROOF /// Who are we reviving? var/mob/living/corpse /// Who if anyone is playing as them? diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm index 5319d4465e2ab..659bcec50cf97 100644 --- a/code/modules/assembly/flash.dm +++ b/code/modules/assembly/flash.dm @@ -171,7 +171,7 @@ visible_message(span_danger("[user] blinds [flashed] with the flash!"), span_userdanger("[user] blinds you with the flash!")) //easy way to make sure that you can only long stun someone who is facing in your direction flashed.adjustStaminaLoss(rand(80, 120) * (1 - (deviation * 0.5))) - flashed.Paralyze(rand(25, 50) * (1 - (deviation * 0.5))) + flashed.Knockdown(rand(25, 50) * (1 - (deviation * 0.5))) SEND_SIGNAL(user, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON, flashed, src, deviation) else if(user) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index 52ef29f38e5d6..de0954c960e5e 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -35,7 +35,7 @@ /obj/item/assembly/voice/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) . = ..() - if(message_mods[WHISPER_MODE]) //Too quiet lad + if(message_mods[WHISPER_MODE] || message_mods[MODE_RELAY]) //Too quiet lad return FALSE if(speaker == src) return FALSE diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index be455ea6d4709..82760e75a6259 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -54,6 +54,10 @@ ///Whether it can be painted var/paintable = TRUE + ///Whether it will generate cap sprites when hidden + var/has_cap_visuals = FALSE + ///Cap overlay that is being added to turf's `vis_contents`, `null` if pipe was never hidden or has no valid connections + var/obj/effect/overlay/cap_visual/cap_overlay ///Is the thing being rebuilt by SSair or not. Prevents list bloat var/rebuilding = FALSE @@ -106,6 +110,10 @@ if(isturf(loc)) turf_loc = loc turf_loc.add_blueprints_preround(src) + + if(hide) + RegisterSignal(src, COMSIG_OBJ_HIDE, PROC_REF(on_hide)) + SSspatial_grid.add_grid_awareness(src, SPATIAL_GRID_CONTENTS_TYPE_ATMOS) SSspatial_grid.add_grid_membership(src, turf_loc, SPATIAL_GRID_CONTENTS_TYPE_ATMOS) if(init_processing) @@ -119,11 +127,22 @@ SSair.stop_processing_machine(src) SSair.rebuild_queue -= src - if(pipe_vision_img) - qdel(pipe_vision_img) + QDEL_NULL(pipe_vision_img) + QDEL_NULL(cap_overlay) return ..() - //return QDEL_HINT_FINDREFERENCE + +/** + * Handler for `COMSIG_OBJ_HIDE`, connects only if `hide` is set to `TRUE`. Calls `update_cap_visuals` on pipe and its connected nodes + */ +/obj/machinery/atmospherics/proc/on_hide(datum/source, underfloor_accessibility) + SHOULD_CALL_PARENT(TRUE) + SIGNAL_HANDLER + + for(var/obj/machinery/atmospherics/node in nodes) + node.update_cap_visuals() + + update_cap_visuals() /** * Run when you update the conditions in which an /atom might want to start reacting to its turf's air @@ -205,8 +224,9 @@ update_appearance() /obj/machinery/atmospherics/update_icon() - . = ..() update_layer() + update_cap_visuals() + return ..() /** * Find a connecting /obj/machinery/atmospherics in specified direction, called by relaymove() @@ -616,6 +636,49 @@ /obj/machinery/atmospherics/proc/update_layer() return +/** + * Handles cap overlay addition and removal, won't do anything if `has_cap_visuals` is set to `FALSE` + */ +/obj/machinery/atmospherics/proc/update_cap_visuals() + if(!has_cap_visuals) + return + + var/turf/our_turf = get_turf(src) + our_turf.vis_contents -= cap_overlay + + var/connections = NONE + for(var/obj/machinery/atmospherics/node in nodes) + if(HAS_TRAIT(node, TRAIT_UNDERFLOOR)) + continue + + if(isplatingturf(get_turf(node))) + continue + + var/connected_dir = get_dir(src, node) + connections |= connected_dir + + if(connections == NONE) + return + + var/bitfield = CARDINAL_TO_PIPECAPS(connections) + bitfield |= ((~connections) & ALL_CARDINALS) + + if(isnull(cap_overlay)) + cap_overlay = new + + SET_PLANE_EXPLICIT(cap_overlay, initial(plane), our_turf) + + cap_overlay.color = pipe_color + cap_overlay.layer = layer + cap_overlay.icon_state = "[bitfield]_[piping_layer]" + + our_turf.vis_contents += cap_overlay + +/obj/effect/overlay/cap_visual + appearance_flags = KEEP_APART + vis_flags = VIS_INHERIT_ID + icon = 'icons/obj/pipes_n_cables/!pipes_bitmask.dmi' + /** * Called by the RPD.dm pre_attack() * Arguments: diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm index e72d72b3d5955..b4e5d88d62c71 100644 --- a/code/modules/atmospherics/machinery/components/components_base.dm +++ b/code/modules/atmospherics/machinery/components/components_base.dm @@ -32,12 +32,6 @@ component_mixture.volume = 200 airs[i] = component_mixture -/obj/machinery/atmospherics/components/Initialize(mapload) - . = ..() - - if(hide) - RegisterSignal(src, COMSIG_OBJ_HIDE, PROC_REF(hide_pipe)) - // Iconnery /** @@ -46,11 +40,14 @@ /obj/machinery/atmospherics/components/proc/update_icon_nopipes() return +/obj/machinery/atmospherics/components/on_hide(datum/source, underfloor_accessibility) + hide_pipe(underfloor_accessibility) + return ..() + /** - * Called in Initialize(), set the showpipe var to true or false depending on the situation, calls update_icon() + * Called in on_hide(), set the showpipe var to true or false depending on the situation, calls update_icon() */ -/obj/machinery/atmospherics/components/proc/hide_pipe(datum/source, underfloor_accessibility) - SIGNAL_HANDLER +/obj/machinery/atmospherics/components/proc/hide_pipe(underfloor_accessibility) showpipe = !!underfloor_accessibility if(showpipe) REMOVE_TRAIT(src, TRAIT_UNDERFLOOR, REF(src)) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm index ea20f2eeb66a8..4161a30ed7d72 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm @@ -10,6 +10,7 @@ hide = TRUE layer = GAS_SCRUBBER_LAYER pipe_state = "injector" + has_cap_visuals = TRUE resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF //really helpful in building gas chambers for xenomorphs idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.25 @@ -74,6 +75,8 @@ if(showpipe) // everything is already shifted so don't shift the cap add_overlay(get_pipe_image(icon, "inje_cap", initialize_directions, pipe_color)) + else + PIPING_LAYER_SHIFT(src, PIPING_LAYER_DEFAULT) if(!nodes[1] || !on || !is_operational) icon_state = "inje_off" diff --git a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm index f461cbe8988f8..17f6c761f129d 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm @@ -10,6 +10,7 @@ shift_underlay_only = FALSE pipe_state = "pvent" + has_cap_visuals = TRUE vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED /obj/machinery/atmospherics/components/unary/passive_vent/update_icon_nopipes() @@ -17,6 +18,8 @@ if(showpipe) var/image/cap = get_pipe_image(icon, "vent_cap", initialize_directions, pipe_color) add_overlay(cap) + else + PIPING_LAYER_SHIFT(src, PIPING_LAYER_DEFAULT) icon_state = "passive_vent" /obj/machinery/atmospherics/components/unary/passive_vent/process_atmos() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm b/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm index d5eada4e73f89..f47d6d5b069ca 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/portables_connector.dm @@ -13,6 +13,8 @@ pipe_flags = PIPING_ONE_PER_TURF pipe_state = "connector" + has_cap_visuals = TRUE + custom_reconcilation = TRUE ///Reference to the connected device @@ -29,11 +31,13 @@ return ..() /obj/machinery/atmospherics/components/unary/portables_connector/update_icon_nopipes() - icon_state = "connector" + cut_overlays() if(showpipe) - cut_overlays() var/image/cap = get_pipe_image(icon, "connector_cap", initialize_directions, pipe_color) add_overlay(cap) + else + PIPING_LAYER_SHIFT(src, PIPING_LAYER_DEFAULT) + icon_state = "connector" /obj/machinery/atmospherics/components/unary/portables_connector/process_atmos() if(!connected_device) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm index 46adfee054e6e..aa890b0b574a0 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm @@ -16,8 +16,6 @@ layer = OBJ_LAYER circuit = /obj/item/circuitboard/machine/thermomachine - hide = TRUE - move_resist = MOVE_RESIST_DEFAULT vent_movement = NONE pipe_flags = PIPING_ONE_PER_TURF diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm index f3c5563fd3afd..bece67572b6f5 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm @@ -14,6 +14,7 @@ hide = TRUE shift_underlay_only = FALSE pipe_state = "uvent" + has_cap_visuals = TRUE vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED // vents are more complex machinery and so are less resistant to damage max_integrity = 100 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 50054417362d2..102728fb59149 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -12,6 +12,7 @@ hide = TRUE shift_underlay_only = FALSE pipe_state = "scrubber" + has_cap_visuals = TRUE vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED processing_flags = NONE diff --git a/code/modules/atmospherics/machinery/pipes/pipe_spritesheet_helper.dm b/code/modules/atmospherics/machinery/pipes/pipe_spritesheet_helper.dm index 9642442a9733f..6b1997cc3c718 100644 --- a/code/modules/atmospherics/machinery/pipes/pipe_spritesheet_helper.dm +++ b/code/modules/atmospherics/machinery/pipes/pipe_spritesheet_helper.dm @@ -18,6 +18,13 @@ "[WEST]"=icon('icons/obj/pipes_n_cables/pipe_template_pieces.dmi', "damage_mask", WEST), ) + var/static/list/icon/cap_masks = list( + "[NORTH]" = icon('icons/obj/pipes_n_cables/pipe_template_pieces.dmi', "cap_mask", NORTH), + "[EAST]" = icon('icons/obj/pipes_n_cables/pipe_template_pieces.dmi', "cap_mask", EAST), + "[SOUTH]" = icon('icons/obj/pipes_n_cables/pipe_template_pieces.dmi', "cap_mask", SOUTH), + "[WEST]" = icon('icons/obj/pipes_n_cables/pipe_template_pieces.dmi', "cap_mask", WEST), + ) + var/icon/generated_icons /datum/pipe_icon_generator/proc/Start(icon_state_suffix="") @@ -85,6 +92,34 @@ outputs[damaged] = "[icon_state_dirs]_[layer]" return outputs +/datum/pipe_icon_generator/proc/generate_capped(icon/working, layer, dirs, x_offset=1, y_offset=1) + var/list/outputs = list() + var/list/completed = list() + for(var/combined_dirs in 1 to 15) + combined_dirs &= dirs + + var/completion_key = "[combined_dirs]" + if(completed[completion_key] || (combined_dirs == NONE)) + continue + + completed[completion_key] = TRUE + + var/icon/capped_mask = icon('icons/obj/pipes_n_cables/pipe_template_pieces.dmi', "blank_mask") + for(var/i in 0 to 3) + var/dir = 1 << i + if(!(combined_dirs & dir)) + continue + + var/icon/cap_mask = cap_masks["[dir]"] + capped_mask.Blend(cap_mask, ICON_OVERLAY, x_offset, y_offset) + + var/icon/capped = icon(working) + capped.Blend(capped_mask, ICON_MULTIPLY) + + var/icon_state_dirs = (dirs & ~combined_dirs) | CARDINAL_TO_PIPECAPS(combined_dirs) + outputs[capped] = "[icon_state_dirs]_[layer]" + + return outputs /datum/pipe_icon_generator/proc/GeneratePipeStraight(icon_state_suffix, layer, combined_dirs) var/list/output = list() @@ -97,8 +132,10 @@ switch(combined_dirs) if(NORTH | SOUTH) output += GenerateDamaged(working, layer, combined_dirs, y_offset=offset) + output += generate_capped(working, layer, combined_dirs, y_offset=offset) if(EAST | WEST) output += GenerateDamaged(working, layer, combined_dirs, x_offset=offset) + output += generate_capped(working, layer, combined_dirs, x_offset=offset) return output @@ -117,6 +154,7 @@ output[working] = "[combined_dirs]_[layer]" output += GenerateDamaged(working, layer, combined_dirs) + output += generate_capped(working, layer, combined_dirs) return output @@ -135,6 +173,7 @@ output[working] = "[combined_dirs]_[layer]" output += GenerateDamaged(working, layer, combined_dirs) + output += generate_capped(working, layer, combined_dirs) return output @@ -144,5 +183,6 @@ output[working] = "[combined_dirs]_[layer]" output += GenerateDamaged(working, layer, combined_dirs) + output += generate_capped(working, layer, combined_dirs) return output diff --git a/code/modules/atmospherics/machinery/pipes/smart.dm b/code/modules/atmospherics/machinery/pipes/smart.dm index 7c530bace5fcf..ce4dc0e36a51e 100644 --- a/code/modules/atmospherics/machinery/pipes/smart.dm +++ b/code/modules/atmospherics/machinery/pipes/smart.dm @@ -10,6 +10,8 @@ GLOBAL_LIST_INIT(atmos_components, typecacheof(list(/obj/machinery/atmospherics) device_type = QUATERNARY construction_type = /obj/item/pipe/quaternary pipe_state = "manifold4w" + has_cap_visuals = TRUE + ///Current active connections var/connections = NONE diff --git a/code/modules/bitrunning/objects/byteforge.dm b/code/modules/bitrunning/objects/byteforge.dm index 5f089bf66ca04..f8212b7666b99 100644 --- a/code/modules/bitrunning/objects/byteforge.dm +++ b/code/modules/bitrunning/objects/byteforge.dm @@ -63,5 +63,5 @@ /obj/machinery/byteforge/proc/start_to_spawn(obj/cache) flicker() - addtimer(CALLBACK(src, PROC_REF(spawn_cache), cache), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) + addtimer(CALLBACK(src, PROC_REF(spawn_cache), cache), 1 SECONDS) diff --git a/code/modules/bitrunning/objects/landmarks.dm b/code/modules/bitrunning/objects/landmarks.dm index 3a90939dae1b2..3b38493edfffa 100644 --- a/code/modules/bitrunning/objects/landmarks.dm +++ b/code/modules/bitrunning/objects/landmarks.dm @@ -22,6 +22,11 @@ name = "Bitrunning crate spawn" icon_state = "crate" +/// Where you want secondary objectives to spawn +/obj/effect/landmark/bitrunning/curiosity_spawn + name = "Bitrunning curiosity spawn" + icon_state = "crate" + ///Swaps the locations of an encrypted crate in the area with another randomly selected crate. ///Randomizes names, so you have to inspect crates manually. /obj/effect/landmark/bitrunning/crate_replacer diff --git a/code/modules/bitrunning/objects/loot_box.dm b/code/modules/bitrunning/objects/loot_box.dm new file mode 100644 index 0000000000000..39209c33d97f9 --- /dev/null +++ b/code/modules/bitrunning/objects/loot_box.dm @@ -0,0 +1,53 @@ +/obj/item/storage/lockbox/bitrunning + name = "base class curiosity" + desc = "Talk to a coder." + req_access = list(ACCESS_INACCESSIBLE) + icon_state = "bitrunning+l" + inhand_icon_state = "bitrunning" + base_icon_state = "bitrunning" + icon_locked = "bitrunning+l" + icon_closed = "bitrunning" + icon_broken = "bitrunning+b" + +/obj/item/storage/lockbox/bitrunning/encrypted + name = "encrypted curiosity" + desc = "Needs to be decrypted at the safehouse to be opened." + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + /// Path for the loot we are assigned + var/loot_path + +/obj/item/storage/lockbox/bitrunning/encrypted/emag_act(mob/user, obj/item/card/emag/emag_card) + return FALSE + +/obj/item/storage/lockbox/bitrunning/decrypted + name = "decrypted curiosity" + desc = "Compiled from the virtual domain. An extra reward of a successful bitrunner." + /// What virtual domain did we come from. + var/datum/lazy_template/virtual_domain/source_domain + +/obj/item/storage/lockbox/bitrunning/decrypted/Initialize( + mapload, + datum/lazy_template/virtual_domain/completed_domain, + ) + + if(isnull(completed_domain)) + log_runtime("Decrypted curiosity was created with no source domain.") + return INITIALIZE_HINT_QDEL + + if(!istype(completed_domain, /datum/lazy_template/virtual_domain)) // Check if this is a proper virtual domain before doing anything with it + log_runtime("Decrypted curiosity was created with an invalid source domain. [completed_domain.name] ([completed_domain.type]).") + return INITIALIZE_HINT_QDEL + + source_domain = completed_domain + + . = ..() + atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL + atom_storage.max_slots = 1 + atom_storage.max_total_storage = 3 + atom_storage.locked = STORAGE_NOT_LOCKED + icon_state = icon_closed + playsound(src, 'sound/magic/blink.ogg', 50, TRUE) + +/obj/item/storage/lockbox/bitrunning/decrypted/PopulateContents() + var/choice = SSbitrunning.pick_secondary_loot(source_domain) + new choice(src) diff --git a/code/modules/bitrunning/objects/loot_crate.dm b/code/modules/bitrunning/objects/loot_crate.dm index a0a74ecb4b921..db3f927e02153 100644 --- a/code/modules/bitrunning/objects/loot_crate.dm +++ b/code/modules/bitrunning/objects/loot_crate.dm @@ -42,11 +42,11 @@ if(isnull(completed_domain)) return - PopulateContents(completed_domain.reward_points, completed_domain.extra_loot, rewards_multiplier) + PopulateContents(completed_domain.reward_points, completed_domain.completion_loot, rewards_multiplier) -/obj/structure/closet/crate/secure/bitrunning/decrypted/PopulateContents(reward_points, list/extra_loot, rewards_multiplier) +/obj/structure/closet/crate/secure/bitrunning/decrypted/PopulateContents(reward_points, list/completion_loot, rewards_multiplier) . = ..() - spawn_loot(extra_loot) + spawn_loot(completion_loot) new /obj/item/stack/ore/iron(src, calculate_loot(reward_points, rewards_multiplier, ORE_MULTIPLIER_IRON)) new /obj/item/stack/ore/glass(src, calculate_loot(reward_points, rewards_multiplier, ORE_MULTIPLIER_GLASS)) @@ -70,16 +70,16 @@ var/random_sum = (rand() + 0.5) * base return ROUND_UP(random_sum * ore_multiplier) -/// Handles spawning extra loot. This tries to handle bad flat and assoc lists -/obj/structure/closet/crate/secure/bitrunning/decrypted/proc/spawn_loot(list/extra_loot) - for(var/path in extra_loot) +/// Handles spawning completion loot. This tries to handle bad flat and assoc lists +/obj/structure/closet/crate/secure/bitrunning/decrypted/proc/spawn_loot(list/completion_loot) + for(var/path in completion_loot) if(!ispath(path)) return FALSE - if(isnull(extra_loot[path])) + if(isnull(completion_loot[path])) return FALSE - for(var/i in 1 to extra_loot[path]) + for(var/i in 1 to completion_loot[path]) new path(src) return TRUE diff --git a/code/modules/bitrunning/objects/quantum_console.dm b/code/modules/bitrunning/objects/quantum_console.dm index 71d7df87f121b..6cc8aef6dae83 100644 --- a/code/modules/bitrunning/objects/quantum_console.dm +++ b/code/modules/bitrunning/objects/quantum_console.dm @@ -56,7 +56,7 @@ if(isnull(server)) return data - data["available_domains"] = server.get_available_domains() + data["available_domains"] = SSbitrunning.get_available_domains(server.scanner_tier, server.points) data["avatars"] = server.get_avatar_data() return data diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm index 62e20367190e6..f3f713768972c 100644 --- a/code/modules/bitrunning/server/_parent.dm +++ b/code/modules/bitrunning/server/_parent.dm @@ -20,8 +20,6 @@ var/is_ready = TRUE /// Chance multipled by threat to spawn a glitch var/glitch_chance = 0.05 - /// List of available domains - var/list/available_domains = list() /// Current plugged in users var/list/datum/weakref/avatar_connection_refs = list() /// Cached list of mutable mobs in zone for cybercops @@ -63,13 +61,9 @@ RegisterSignals(src, list(COMSIG_MACHINERY_BROKEN, COMSIG_MACHINERY_POWER_LOST), PROC_REF(on_broken)) RegisterSignal(src, COMSIG_QDELETING, PROC_REF(on_delete)) - // This further gets sorted in the client by cost so it's random and grouped - available_domains = shuffle(subtypesof(/datum/lazy_template/virtual_domain)) - /obj/machinery/quantum_server/Destroy(force) . = ..() - available_domains.Cut() mutation_candidate_refs.Cut() avatar_connection_refs.Cut() spawned_threat_refs.Cut() diff --git a/code/modules/bitrunning/server/loot.dm b/code/modules/bitrunning/server/loot.dm index 0aab2a86ff429..cb4902abfe3ab 100644 --- a/code/modules/bitrunning/server/loot.dm +++ b/code/modules/bitrunning/server/loot.dm @@ -40,6 +40,15 @@ chosen_forge.start_to_spawn(reward_cache) return TRUE +/obj/machinery/quantum_server/proc/generate_secondary_loot(obj/curiosity, obj/machinery/byteforge/chosen_forge) + spark_at_location(curiosity) // abracadabra! + qdel(curiosity) // and it's gone! + + var/obj/item/storage/lockbox/bitrunning/decrypted/reward_curiosity = new(src, generated_domain) + + chosen_forge.start_to_spawn(reward_curiosity) + return TRUE + /// Returns the markdown text containing domain completion information /obj/machinery/quantum_server/proc/get_completion_certificate() var/base_points = generated_domain.reward_points diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm index 5018e464d6a1f..2ddff04fc1535 100644 --- a/code/modules/bitrunning/server/map_handling.dm +++ b/code/modules/bitrunning/server/map_handling.dm @@ -67,9 +67,9 @@ /// Initializes a new domain if the given key is valid and the user has enough points /obj/machinery/quantum_server/proc/load_domain(map_key) - for(var/datum/lazy_template/virtual_domain/available as anything in subtypesof(/datum/lazy_template/virtual_domain)) - if(map_key == initial(available.key) && points >= initial(available.cost)) - generated_domain = new available() + for(var/datum/lazy_template/virtual_domain/available in SSbitrunning.all_domains) + if(map_key == available.key && points >= available.cost) + generated_domain = available RegisterSignal(generated_domain, COMSIG_LAZY_TEMPLATE_LOADED, PROC_REF(on_template_loaded)) generated_domain.lazy_load() return TRUE @@ -80,6 +80,7 @@ /obj/machinery/quantum_server/proc/load_map_items() var/turf/goal_turfs = list() var/turf/cache_turfs = list() + var/turf/curiosity_turfs = list() for(var/obj/effect/landmark/bitrunning/thing in GLOB.landmarks_list) if(istype(thing, /obj/effect/landmark/bitrunning/hololadder_spawn)) @@ -100,6 +101,11 @@ qdel(thing) continue + if(istype(thing, /obj/effect/landmark/bitrunning/curiosity_spawn)) + curiosity_turfs += get_turf(thing) + qdel(thing) + continue + if(istype(thing, /obj/effect/landmark/bitrunning/loot_signal)) var/turf/signaler_turf = get_turf(thing) signaler_turf.AddComponent(/datum/component/bitrunning_points, generated_domain) @@ -113,6 +119,13 @@ if(!attempt_spawn_cache(cache_turfs)) return FALSE + while(length(curiosity_turfs)) + var/turf/picked_turf = attempt_spawn_curiosity(curiosity_turfs) + if(!picked_turf) + break + generated_domain.secondary_loot_generated += 1 + curiosity_turfs -= picked_turf + return TRUE /// Stops the current virtual domain and disconnects all users @@ -151,6 +164,8 @@ creature.dust(just_ash = TRUE, force = TRUE) // sometimes mobs just don't die + generated_domain.secondary_loot_generated = 0 + avatar_connection_refs.Cut() exit_turfs = list() generated_domain = null diff --git a/code/modules/bitrunning/server/obj_generation.dm b/code/modules/bitrunning/server/obj_generation.dm index 38fee74d4a82f..ab17682fb6f86 100644 --- a/code/modules/bitrunning/server/obj_generation.dm +++ b/code/modules/bitrunning/server/obj_generation.dm @@ -15,6 +15,26 @@ new /obj/structure/closet/crate/secure/bitrunning/encrypted(chosen_turf) return TRUE +/// Attempts to spawn a lootbox +/obj/machinery/quantum_server/proc/attempt_spawn_curiosity(list/possible_turfs) + if(!length(possible_turfs)) // Out of turfs to place a curiosity + return FALSE + + if(generated_domain.secondary_loot_generated >= assoc_value_sum(generated_domain.secondary_loot)) // Out of curiosities to place + return FALSE + + shuffle_inplace(possible_turfs) + var/turf/chosen_turf = validate_turf(pick(possible_turfs)) + + if(isnull(chosen_turf)) + possible_turfs.Remove(chosen_turf) + chosen_turf = validate_turf(pick(possible_turfs)) + if(isnull(chosen_turf)) + CRASH("vdom: after two attempts, could not find a valid turf for curiosity") + + new /obj/item/storage/lockbox/bitrunning/encrypted(chosen_turf) + return chosen_turf + /// Generates a new avatar for the bitrunner. /obj/machinery/quantum_server/proc/generate_avatar(obj/structure/hololadder/wayout, datum/outfit/netsuit) var/mob/living/carbon/human/avatar = new(wayout.loc) diff --git a/code/modules/bitrunning/server/signal_handlers.dm b/code/modules/bitrunning/server/signal_handlers.dm index 81db7a5b9b0a6..0e5e949e8bb51 100644 --- a/code/modules/bitrunning/server/signal_handlers.dm +++ b/code/modules/bitrunning/server/signal_handlers.dm @@ -47,6 +47,10 @@ generate_loot(arrived, chosen_forge) return + if(istype(arrived, /obj/item/storage/lockbox/bitrunning/encrypted)) + generate_secondary_loot(arrived, chosen_forge, generated_domain) + return + /// Handles examining the server. Shows cooldown time and efficiency. /obj/machinery/quantum_server/proc/on_goal_turf_examined(datum/source, mob/examiner, list/examine_text) SIGNAL_HANDLER diff --git a/code/modules/bitrunning/server/threats.dm b/code/modules/bitrunning/server/threats.dm index 3ed4ad45bc668..6c42322d0cf01 100644 --- a/code/modules/bitrunning/server/threats.dm +++ b/code/modules/bitrunning/server/threats.dm @@ -69,16 +69,15 @@ var/datum/antagonist/bitrunning_glitch/chosen_role = forced_role || get_antagonist_role() var/role_name = initial(chosen_role.name) - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(spawn_glitch), chosen_role, mutation_target) - mutation_target.AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_GLITCH, \ - job_bans = ROLE_GLITCH, \ - to_call = to_call, \ - title = role_name, \ - header = "Bitrunning Malfunction", \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + check_jobban = ROLE_GLITCH, + poll_time = 20 SECONDS, + checked_target = mutation_target, + ignore_category = POLL_IGNORE_GLITCH, + alert_pic = mutation_target, + role_name_text = "Bitrunning Malfunction: [role_name]", ) - + spawn_glitch(chosen_role, mutation_target, chosen_one) return mutation_target /// Orbit poll has concluded - spawn the antag diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm index a6069d45e90a3..24ed78210cf65 100644 --- a/code/modules/bitrunning/server/util.dm +++ b/code/modules/bitrunning/server/util.dm @@ -1,4 +1,3 @@ -#define REDACTED "???" #define MAX_DISTANCE 4 // How far crates can spawn from the server /// Resets the cooldown state and updates icons @@ -7,28 +6,6 @@ update_appearance() radio.talk_into(src, "Thermal systems within operational parameters. Proceeding to domain configuration.", RADIO_CHANNEL_SUPPLY) -/// Compiles a list of available domains. -/obj/machinery/quantum_server/proc/get_available_domains() - var/list/levels = list() - - for(var/datum/lazy_template/virtual_domain/domain as anything in available_domains) - if(initial(domain.test_only)) - continue - var/can_view = initial(domain.difficulty) < scanner_tier && initial(domain.cost) <= points + 5 - var/can_view_reward = initial(domain.difficulty) < (scanner_tier + 1) && initial(domain.cost) <= points + 3 - - levels += list(list( - "cost" = initial(domain.cost), - "desc" = can_view ? initial(domain.desc) : "Limited scanning capabilities. Cannot infer domain details.", - "difficulty" = initial(domain.difficulty), - "id" = initial(domain.key), - "is_modular" = initial(domain.is_modular), - "name" = can_view ? initial(domain.name) : REDACTED, - "reward" = can_view_reward ? initial(domain.reward_points) : REDACTED, - )) - - return levels - /// If there are hosted minds, attempts to get a list of their current virtual bodies w/ vitals /obj/machinery/quantum_server/proc/get_avatar_data() var/list/hosted_avatars = list() @@ -120,5 +97,4 @@ if(!tile.is_blocked_turf()) return chosen_turf -#undef REDACTED #undef MAX_DISTANCE diff --git a/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm b/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm index 6475a20a0c758..55e6d08a147d5 100644 --- a/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm +++ b/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_MEDIUM desc = "Board an abductor ship and take their goodies." difficulty = BITRUNNER_DIFFICULTY_MEDIUM - extra_loot = list(/obj/item/toy/plush/abductor/agent = 1) + completion_loot = list(/obj/item/toy/plush/abductor/agent = 1) help_text = "An abductor mothership unknowingly entered a hostile environment. \ They are currently preparing to escape the area with their gear and loot including \ the crate. Be careful, they are known for their advanced weaponry." diff --git a/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm b/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm index ae00fc56cf794..80f07448b69fb 100644 --- a/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm +++ b/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm @@ -1,7 +1,7 @@ /datum/lazy_template/virtual_domain/beach_bar name = "Beach Bar" desc = "A cheerful seaside haven where friendly skeletons serve up drinks. Say, how'd you guys get so dead?" - extra_loot = list(/obj/item/toy/beach_ball = 1) + completion_loot = list(/obj/item/toy/beach_ball = 1) help_text = "This place is running on a skeleton crew, and they don't seem to be too keen to share details. \ Maybe a few drinks of liquid charm will get the spirits up. As the saying goes, if you can't beat 'em, join 'em." key = "beach_bar" diff --git a/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm b/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm index f7f58273d89dc..ab8b282cfbf7d 100644 --- a/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm +++ b/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_HIGH desc = "King of the slaughter demons. Bubblegum is a massive, hulking beast with a penchant for violence." difficulty = BITRUNNER_DIFFICULTY_HIGH - extra_loot = list(/obj/item/toy/plush/bubbleplush = 1) + completion_loot = list(/obj/item/toy/plush/bubbleplush = 1) forced_outfit = /datum/outfit/job/miner key = "bubblegum" map_name = "bubblegum" diff --git a/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm b/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm index f1bf44686088f..3aff298efdb5a 100644 --- a/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm +++ b/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_LOW desc = "In the deep, dark reaches of space, there is only Honk." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/bikehorn = 1) + completion_loot = list(/obj/item/bikehorn = 1) forced_outfit = /datum/outfit/job/clown help_text = "The trials of the Honkitude have begun. The sound of bike horns wailing in the distance. \ this realm- some sort of puzzle, has existed in legend as the final test of just how silly you are." diff --git a/code/modules/bitrunning/virtual_domain/domains/pipedream.dm b/code/modules/bitrunning/virtual_domain/domains/pipedream.dm index 3eb23b94386ce..595600ce71c4c 100644 --- a/code/modules/bitrunning/virtual_domain/domains/pipedream.dm +++ b/code/modules/bitrunning/virtual_domain/domains/pipedream.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_LOW desc = "An abandoned and infested factory manufacturing disposal pipes." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/stack/pipe_cleaner_coil/random/five = 1) + completion_loot = list(/obj/item/stack/pipe_cleaner_coil/random/five = 1) help_text = "Not long ago, this place was thriving with activity. The workers \ seemed to have left in a hurry, and now productivity is in the bin. Something \ must have trashed the place, but what?" diff --git a/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm b/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm index 9cb299055dd7d..2d4d2c5ee362a 100644 --- a/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm +++ b/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm @@ -4,7 +4,7 @@ desc = "Another neglected corner of the virtual world. This one had to be abandoned due to zombie virus. \ Warning -- Virtual domain does not support visual display. This mission must be completed using echolocation." difficulty = BITRUNNER_DIFFICULTY_MEDIUM - extra_loot = list(/obj/item/radio/headset/psyker = 1) //Looks cool, might make your local burdened chaplain happy. + completion_loot = list(/obj/item/radio/headset/psyker = 1) //Looks cool, might make your local burdened chaplain happy. forced_outfit = /datum/outfit/echolocator help_text = "This once-beloved virtual domain has been corrupted by a virus, rendering it unstable, full of holes, and full of ZOMBIES! \ There should be a Mystery Box nearby to help get you armed. Get armed, and finish what the cyber-police started!" diff --git a/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm b/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm index 1c79d8fd21d1c..4d100482429b9 100644 --- a/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm +++ b/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm @@ -6,7 +6,8 @@ instead of ladders its stairs and instead of snakes its a steep drop down a \ cliff into rough rocks or liquid plasma." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/clothing/suit/costume/snowman = 2) + completion_loot = list(/obj/item/clothing/suit/costume/snowman = 2) + secondary_loot = list(/obj/item/clothing/shoes/wheelys/skishoes = 2, /obj/item/clothing/head/costume/ushanka/polar = 1) forced_outfit = /datum/outfit/job/virtual_domain_iceclimber key = "stairs_and_cliffs" map_name = "stairs_and_cliffs" diff --git a/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm b/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm index 24fa6e5482ac6..5f754dd433ad6 100644 --- a/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm +++ b/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_MEDIUM desc = "Board the enemy ship and recover the stolen cargo." difficulty = BITRUNNER_DIFFICULTY_MEDIUM - extra_loot = list(/obj/item/toy/plush/nukeplushie = 1) + completion_loot = list(/obj/item/toy/plush/nukeplushie = 1) help_text = "A group of Syndicate operatives have stolen valuable cargo from the station. \ They have boarded their ship and are attempting to escape. Infiltrate their ship and recover \ the crate. Be careful, they are extremely armed." diff --git a/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm b/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm index b704441edaa84..5da6449ccf924 100644 --- a/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm +++ b/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_EXTREME desc = "Suspended in the silent void of space, the Neon Relic is a haunting echo of a retro-futuristic era. Hang out, enjoy the view." difficulty = BITRUNNER_DIFFICULTY_NONE - extra_loot = list(/obj/item/stack/spacecash/c500 = 4) + completion_loot = list(/obj/item/stack/spacecash/c500 = 4) key = "vaporwave" map_name = "vaporwave" reward_points = BITRUNNER_REWARD_EXTREME diff --git a/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm b/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm index ed708d0592cb1..6b76956eacc70 100644 --- a/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm +++ b/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_LOW desc = "Our ship scanners have detected lifeforms of unknown origin. Friendly attempts to contact them have failed." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/toy/plush/rouny = 1) + completion_loot = list(/obj/item/toy/plush/rouny = 1) help_text = "You are on a barren planet filled with hostile creatures. There is a crate here, not hidden, \ simply protected. Expect resistance." is_modular = TRUE diff --git a/code/modules/bitrunning/virtual_domain/virtual_domain.dm b/code/modules/bitrunning/virtual_domain/virtual_domain.dm index 838834f45a74a..b316bb97cae1e 100644 --- a/code/modules/bitrunning/virtual_domain/virtual_domain.dm +++ b/code/modules/bitrunning/virtual_domain/virtual_domain.dm @@ -6,6 +6,7 @@ map_dir = "_maps/virtual_domains" map_name = "None" key = "Virtual Domain" + place_on_top = TRUE /// Cost of this map to load var/cost = BITRUNNER_COST_NONE @@ -28,7 +29,11 @@ /// Byond will look for modular mob segment landmarks then choose from here at random. You can make them unique also. var/list/datum/modular_mob_segment/mob_modules = list() /// An assoc list of typepath/amount to spawn on completion. Not weighted - the value is the amount - var/list/extra_loot + var/list/completion_loot + /// An accoc list of typepath/amount to spawn from secondary objectives. Not weighted - the value is the total number of items that can be obtained. + var/list/secondary_loot = list() + /// Number of secondary loot boxes generated. Resets when the domain is reloaded. + var/secondary_loot_generated /// Forces all mob modules to only load once var/modular_unique_mobs = FALSE // Name to show in the UI diff --git a/code/modules/cargo/coupon.dm b/code/modules/cargo/coupon.dm index f654db448872e..4c5e56a7d4119 100644 --- a/code/modules/cargo/coupon.dm +++ b/code/modules/cargo/coupon.dm @@ -84,7 +84,7 @@ to_chat(cursed, span_warning("The coupon reads 'fuck you' in large, bold text... is- is that a prize, or?")) if(!cursed.GetComponent(/datum/component/omen)) - cursed.AddComponent(/datum/component/omen) + cursed.AddComponent(/datum/component/omen, 1) return TRUE if(HAS_TRAIT(cursed, TRAIT_CURSED)) to_chat(cursed, span_warning("What a horrible night... To have a curse!")) diff --git a/code/modules/cargo/exports/parts.dm b/code/modules/cargo/exports/parts.dm index 840d40f183712..fc8c9656fea78 100644 --- a/code/modules/cargo/exports/parts.dm +++ b/code/modules/cargo/exports/parts.dm @@ -33,3 +33,9 @@ unit_name = "data disk" export_types = list(/obj/item/computer_disk) include_subtypes = TRUE + +/datum/export/refill_canister + cost = CARGO_CRATE_VALUE * 0.5 //If someone want to make this worth more as it empties, go ahead + unit_name = "vending refill canister" + message = "Thank you for restocking the station!" + export_types = list(/obj/item/vending_refill) diff --git a/code/modules/cargo/markets/market_uplink.dm b/code/modules/cargo/markets/market_uplink.dm index 19c1a049a1be0..a82218082e90d 100644 --- a/code/modules/cargo/markets/market_uplink.dm +++ b/code/modules/cargo/markets/market_uplink.dm @@ -150,6 +150,7 @@ icon_state = "uplink" //The original black market uplink accessible_markets = list(/datum/market/blackmarket) + custom_premium_price = PAYCHECK_CREW * 2.5 /datum/crafting_recipe/blackmarket_uplink diff --git a/code/modules/cargo/materials_market.dm b/code/modules/cargo/materials_market.dm index 92d83d5d0a141..947197d16f298 100644 --- a/code/modules/cargo/materials_market.dm +++ b/code/modules/cargo/materials_market.dm @@ -166,12 +166,14 @@ var/min_value_override = initial(traded_mat.minimum_value_override) if(min_value_override) minimum_value_threshold = min_value_override - + else + minimum_value_threshold = round(initial(traded_mat.value_per_unit) * SHEET_MATERIAL_AMOUNT * 0.5) //send data material_data += list(list( "name" = initial(traded_mat.name), "price" = SSstock_market.materials_prices[traded_mat], + "rarity" = initial(traded_mat.value_per_unit), "threshold" = minimum_value_threshold, "quantity" = SSstock_market.materials_quantity[traded_mat], "trend" = trend_string, @@ -205,6 +207,7 @@ .["orderBalance"] = current_cost .["orderingPrive"] = ordering_private .["canOrderCargo"] = can_buy_via_budget + .["updateTime"] = SSstock_market.next_fire - world.time /obj/machinery/materials_market/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) . = ..() @@ -348,8 +351,8 @@ /obj/item/stock_block/Initialize(mapload) . = ..() - addtimer(CALLBACK(src, PROC_REF(value_warning)), 2.5 MINUTES, TIMER_DELETE_ME) - addtimer(CALLBACK(src, PROC_REF(update_value)), 5 MINUTES, TIMER_DELETE_ME) + addtimer(CALLBACK(src, PROC_REF(value_warning)), 1.5 MINUTES, TIMER_DELETE_ME) + addtimer(CALLBACK(src, PROC_REF(update_value)), 3 MINUTES, TIMER_DELETE_ME) /obj/item/stock_block/examine(mob/user) . = ..() diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm index f0de463c4490f..7fa9013c686cf 100644 --- a/code/modules/cargo/packs/science.dm +++ b/code/modules/cargo/packs/science.dm @@ -104,14 +104,16 @@ name = "Robotics Assembly Crate" desc = "The tools you need to replace those finicky humans with a loyal robot army! \ Contains four proximity sensors, two empty first aid kits, two health analyzers, \ - two red hardhats, two mechanical toolboxes, and two cleanbot assemblies!" + two red hardhats, two toolboxes, and two cleanbot assemblies!" cost = CARGO_CRATE_VALUE * 3 access = ACCESS_ROBOTICS access_view = ACCESS_ROBOTICS - contains = list(/obj/item/assembly/prox_sensor = 5, + contains = list(/obj/item/assembly/prox_sensor = 4, /obj/item/healthanalyzer = 2, /obj/item/clothing/head/utility/hardhat/red = 2, - /obj/item/storage/medkit = 2) + /obj/item/storage/medkit = 2, + /obj/item/storage/toolbox = 2, + /obj/item/bot_assembly/cleanbot = 2) crate_name = "robotics assembly crate" crate_type = /obj/structure/closet/crate/secure/science/robo diff --git a/code/modules/cargo/packs/vending_restock.dm b/code/modules/cargo/packs/vending_restock.dm index cfe9961cc3a43..10ae874d5d6c9 100644 --- a/code/modules/cargo/packs/vending_restock.dm +++ b/code/modules/cargo/packs/vending_restock.dm @@ -4,7 +4,7 @@ /datum/supply_pack/vending/bartending name = "Booze-o-mat and Coffee Supply Crate" desc = "Bring on the booze and coffee vending machine refills." - cost = CARGO_CRATE_VALUE * 4 + cost = CARGO_CRATE_VALUE * 2 contains = list(/obj/item/vending_refill/boozeomat, /obj/item/vending_refill/coffee, ) @@ -14,7 +14,7 @@ name = "Cigarette Supply Crate" desc = "Don't believe the reports - smoke today! Contains a \ cigarette vending machine refill." - cost = CARGO_CRATE_VALUE * 3 + cost = CARGO_CRATE_VALUE * 2 contains = list(/obj/item/vending_refill/cigarette) crate_name = "cigarette supply crate" crate_type = /obj/structure/closet/crate @@ -62,7 +62,7 @@ /datum/supply_pack/vending/imported name = "Imported Vending Machines" desc = "Vending machines famous in other parts of the galaxy." - cost = CARGO_CRATE_VALUE * 8 + cost = CARGO_CRATE_VALUE * 5 contains = list(/obj/item/vending_refill/sustenance, /obj/item/vending_refill/robotics, /obj/item/vending_refill/sovietsoda, @@ -74,7 +74,7 @@ name = "Medical Vending Crate" desc = "Contains one NanoMed Plus refill, one NanoDrug Plus refill, \ and one wall-mounted NanoMed refill." - cost = CARGO_CRATE_VALUE * 5 + cost = CARGO_CRATE_VALUE * 3.5 contains = list(/obj/item/vending_refill/medical, /obj/item/vending_refill/drugs, /obj/item/vending_refill/wallmed, @@ -85,7 +85,7 @@ name = "PTech Supply Crate" desc = "Not enough cartridges after half the crew lost their PDA \ to explosions? This may fix it." - cost = CARGO_CRATE_VALUE * 3 + cost = CARGO_CRATE_VALUE * 2.5 contains = list(/obj/item/vending_refill/cart) crate_name = "\improper PTech supply crate" @@ -103,7 +103,7 @@ name = "Snack Supply Crate" desc = "One vending machine refill of cavity-bringin' goodness! \ The number one dentist recommended order!" - cost = CARGO_CRATE_VALUE * 3 + cost = CARGO_CRATE_VALUE * 2 contains = list(/obj/item/vending_refill/snack) crate_name = "snacks supply crate" @@ -111,14 +111,14 @@ name = "Softdrinks Supply Crate" desc = "Got whacked by a toolbox, but you still have those pesky teeth? \ Get rid of those pearly whites with this soda machine refill, today!" - cost = CARGO_CRATE_VALUE * 3 + cost = CARGO_CRATE_VALUE * 2 contains = list(/obj/item/vending_refill/cola) crate_name = "soft drinks supply crate" /datum/supply_pack/vending/vendomat name = "Part-Mart & YouTool Supply Crate" desc = "More tools for your IED testing facility." - cost = CARGO_CRATE_VALUE * 2 + cost = CARGO_CRATE_VALUE * 3 contains = list(/obj/item/vending_refill/assist, /obj/item/vending_refill/youtool, ) @@ -138,7 +138,7 @@ name = "Autodrobe Supply Crate" desc = "Autodrobe missing your favorite dress? Solve that issue today \ with this autodrobe refill." - cost = CARGO_CRATE_VALUE * 3 + cost = CARGO_CRATE_VALUE * 2 contains = list(/obj/item/vending_refill/autodrobe) crate_name = "autodrobe supply crate" @@ -200,7 +200,7 @@ name = "Science Wardrobe Supply Crate" desc = "This crate contains refills for the SciDrobe, \ GeneDrobe, and RoboDrobe." - cost = CARGO_CRATE_VALUE * 3 + cost = CARGO_CRATE_VALUE * 4.5 contains = list(/obj/item/vending_refill/wardrobe/robo_wardrobe, /obj/item/vending_refill/wardrobe/gene_wardrobe, /obj/item/vending_refill/wardrobe/science_wardrobe, diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 5de1341358230..1ba96157dc89f 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -1164,8 +1164,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if(!CONFIG_GET(flag/use_age_restriction_for_jobs)) return 0 - if(!isnum(player_age)) - return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced + if(!isnum(player_age) || player_age < 0) + return 0 if(!isnum(days_needed)) return 0 diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 25267e92eff9c..1009cc8a6afd7 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -10,12 +10,15 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") to_chat(usr, span_danger("Speech is currently admin-disabled.")) return - if(!mob) - return + var/client_initalized = VALIDATE_CLIENT_INITIALIZATION(src) + if(isnull(mob) || !client_initalized) + if(!client_initalized) + unvalidated_client_error() // we only want to throw this warning message when it's directly related to client failure. - VALIDATE_CLIENT(src) + to_chat(usr, span_warning("Failed to send your OOC message. You attempted to send the following message:\n[span_big(msg)]")) + return - if(!holder) + if(isnull(holder)) if(!GLOB.ooc_allowed) to_chat(src, span_danger("OOC is globally muted.")) return diff --git a/code/modules/clothing/belts/polymorph_belt.dm b/code/modules/clothing/belts/polymorph_belt.dm index 73959d6d41519..fb09b2e68c8f1 100644 --- a/code/modules/clothing/belts/polymorph_belt.dm +++ b/code/modules/clothing/belts/polymorph_belt.dm @@ -63,10 +63,9 @@ if (target_mob.mob_biotypes & (MOB_HUMANOID|MOB_ROBOTIC|MOB_SPECIAL|MOB_SPIRIT|MOB_UNDEAD)) balloon_alert(user, "incompatible!") return TRUE - if (isanimal_or_basicmob(target_mob)) - if (!target_mob.compare_sentience_type(SENTIENCE_ORGANIC)) - balloon_alert(user, "target too intelligent!") - return TRUE + if (!target_mob.compare_sentience_type(SENTIENCE_ORGANIC)) + balloon_alert(user, "target too intelligent!") + return TRUE if (stored_mob_type == target_mob.type) balloon_alert(user, "already scanned!") return TRUE diff --git a/code/modules/clothing/head/hat.dm b/code/modules/clothing/head/hat.dm index 52e0ca53ac976..846f49ad957cd 100644 --- a/code/modules/clothing/head/hat.dm +++ b/code/modules/clothing/head/hat.dm @@ -312,6 +312,13 @@ to_chat(user, span_notice("You lower the ear flaps on the ushanka.")) earflaps = !earflaps +/obj/item/clothing/head/costume/ushanka/polar + name = "bear hunter's ushanka" + desc = "Handcrafted in Siberia from real polar bears." + icon_state = "ushankadown_polar" + upsprite = "ushankaup_polar" + downsprite = "ushankadown_polar" + /obj/item/clothing/head/costume/nightcap/blue name = "blue nightcap" desc = "A blue nightcap for all the dreamers and snoozers out there." diff --git a/code/modules/clothing/head/mind_monkey_helmet.dm b/code/modules/clothing/head/mind_monkey_helmet.dm index 50ac5bb8ba254..449df33550560 100644 --- a/code/modules/clothing/head/mind_monkey_helmet.dm +++ b/code/modules/clothing/head/mind_monkey_helmet.dm @@ -47,19 +47,18 @@ playsound(src, 'sound/machines/ping.ogg', 30, TRUE) RegisterSignal(magnification, COMSIG_SPECIES_LOSS, PROC_REF(make_fall_off)) polling = TRUE - var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a mind magnified monkey?", check_jobban = ROLE_MONKEY_HELMET, poll_time = 5 SECONDS, target_mob = magnification, ignore_category = POLL_IGNORE_MONKEY_HELMET, pic_source = magnification, role_name_text = "mind-magnified monkey") + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = ROLE_MONKEY_HELMET, poll_time = 5 SECONDS, checked_target = magnification, ignore_category = POLL_IGNORE_MONKEY_HELMET, alert_pic = magnification, role_name_text = "mind-magnified monkey") polling = FALSE if(!magnification) return - if(!candidates.len) + if(isnull(chosen_one)) UnregisterSignal(magnification, COMSIG_SPECIES_LOSS) magnification = null visible_message(span_notice("[src] falls silent and drops on the floor. Maybe you should try again later?")) playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE) user.dropItemToGround(src) return - var/mob/picked = pick(candidates) - magnification.key = picked.key + magnification.key = chosen_one.key playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE) to_chat(magnification, span_notice("You're a mind magnified monkey! Protect your helmet with your life- if you lose it, your sentience goes with it!")) var/policy = get_policy(ROLE_MONKEY_HELMET) diff --git a/code/modules/clothing/shoes/clown.dm b/code/modules/clothing/shoes/clown.dm index aff47fde7fabe..76395a56efd5e 100644 --- a/code/modules/clothing/shoes/clown.dm +++ b/code/modules/clothing/shoes/clown.dm @@ -53,3 +53,9 @@ desc = "The adorable sound they make when you walk will mean making friends is more likely." icon_state = "meown_shoes" squeak_sound = list('sound/effects/footstep/meowstep1.ogg'=1) //mew mew mew mew + +/obj/item/clothing/shoes/clown_shoes/moffers + name = "moffers" + desc = "No moths were harmed in the making of these slippers." + icon_state = "moffers" + squeak_sound = list('sound/effects/footstep/moffstep01.ogg'=1) //like sweet music to my ears diff --git a/code/modules/clothing/suits/costume.dm b/code/modules/clothing/suits/costume.dm index d004b862fcdbf..9cf86a396e95a 100644 --- a/code/modules/clothing/suits/costume.dm +++ b/code/modules/clothing/suits/costume.dm @@ -362,6 +362,48 @@ clothing_flags = THICKMATERIAL flags_inv = HIDEHAIR|HIDEEARS +/obj/item/clothing/suit/hooded/shark_costume // Blahaj + name = "Shark costume" + desc = "Finally, a costume to match your favorite plush." + icon_state = "shark" + icon = 'icons/obj/clothing/suits/costume.dmi' + worn_icon = 'icons/mob/clothing/suits/costume.dmi' + inhand_icon_state = "shark" + body_parts_covered = CHEST|GROIN|ARMS + clothing_flags = THICKMATERIAL + hoodtype = /obj/item/clothing/head/hooded/shark_hood + +/obj/item/clothing/head/hooded/shark_hood + name = "shark hood" + desc = "A hood attached to a shark costume." + icon = 'icons/obj/clothing/head/costume.dmi' + worn_icon = 'icons/mob/clothing/head/costume.dmi' + icon_state = "shark" + body_parts_covered = HEAD + clothing_flags = THICKMATERIAL + flags_inv = HIDEHAIR|HIDEEARS + +/obj/item/clothing/suit/hooded/shork_costume // Oh God Why + name = "shork costume" + desc = "Why would you ever do this?" + icon_state = "sharkcursed" + icon = 'icons/obj/clothing/suits/costume.dmi' + worn_icon = 'icons/mob/clothing/suits/costume.dmi' + inhand_icon_state = "sharkcursed" + body_parts_covered = CHEST|GROIN|ARMS + clothing_flags = THICKMATERIAL + hoodtype = /obj/item/clothing/head/hooded/shork_hood + +/obj/item/clothing/head/hooded/shork_hood + name = "shork hood" + desc = "A hood attached to a shork costume." + icon = 'icons/obj/clothing/head/costume.dmi' + worn_icon = 'icons/mob/clothing/head/costume.dmi' + icon_state = "sharkcursed" + body_parts_covered = HEAD + clothing_flags = THICKMATERIAL + flags_inv = HIDEHAIR|HIDEEARS + /obj/item/clothing/suit/hooded/bloated_human //OH MY GOD WHAT HAVE YOU DONE!?!?!? name = "bloated human suit" desc = "A horribly bloated suit made from human skins." diff --git a/code/modules/deathmatch/deathmatch_maps.dm b/code/modules/deathmatch/deathmatch_maps.dm index d437bffbb3cd7..71d7e8a8651f4 100644 --- a/code/modules/deathmatch/deathmatch_maps.dm +++ b/code/modules/deathmatch/deathmatch_maps.dm @@ -1,6 +1,8 @@ /datum/lazy_template/deathmatch //deathmatch maps that have any possibility of the walls being destroyed should use indestructible walls, because baseturf moment - var/name map_dir = "_maps/map_files/Deathmatch" + place_on_top = TRUE + /// Map UI Name + var/name /// Map Description var/desc = "" var/min_players = 2 diff --git a/code/modules/events/ghost_role/abductor.dm b/code/modules/events/ghost_role/abductor.dm index 65fe4a142f5a6..dfa20885f0c29 100644 --- a/code/modules/events/ghost_role/abductor.dm +++ b/code/modules/events/ghost_role/abductor.dm @@ -14,7 +14,7 @@ fakeable = FALSE //Nothing to fake here /datum/round_event/ghost_role/abductor/spawn_role() - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ABDUCTOR, role = ROLE_ABDUCTOR, pic_source = /obj/item/melee/baton/abductor, role_name_text = role_name) + var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ABDUCTOR, role = ROLE_ABDUCTOR, alert_pic = /obj/item/melee/baton/abductor, role_name_text = role_name, amount_to_pick = 2) if(candidates.len < 2) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/ghost_role/alien_infestation.dm b/code/modules/events/ghost_role/alien_infestation.dm index f4078e52e6588..88e79fd7d60c3 100644 --- a/code/modules/events/ghost_role/alien_infestation.dm +++ b/code/modules/events/ghost_role/alien_infestation.dm @@ -62,7 +62,7 @@ message_admins("An event attempted to spawn an alien but no suitable vents were found. Shutting down.") return MAP_ERROR - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /mob/living/carbon/alien/larva, role_name_text = role_name) + var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, alert_pic = /mob/living/carbon/alien/larva, role_name_text = role_name) if(!candidates.len) return NOT_ENOUGH_PLAYERS diff --git a/code/modules/events/ghost_role/blob.dm b/code/modules/events/ghost_role/blob.dm index 70640512699d4..8e83351f5c045 100644 --- a/code/modules/events/ghost_role/blob.dm +++ b/code/modules/events/ghost_role/blob.dm @@ -33,10 +33,10 @@ blob_icon.Blend("#9ACD32", ICON_MULTIPLY) blob_icon.Blend(icon('icons/mob/nonhuman-player/blob.dmi', "blob_core_overlay"), ICON_OVERLAY) var/image/blob_image = image(blob_icon) - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_BLOB, role = ROLE_BLOB, pic_source = blob_image, role_name_text = role_name) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_BLOB, role = ROLE_BLOB, alert_pic = blob_image, role_name_text = role_name, amount_to_pick = 1, chat_text_border_icon = blob_image) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - var/mob/dead/observer/new_blob = pick(candidates) + var/mob/dead/observer/new_blob = chosen_one var/mob/camera/blob/BC = new_blob.become_overmind() spawned_mobs += BC message_admins("[ADMIN_LOOKUPFLW(BC)] has been made into a blob overmind by an event.") diff --git a/code/modules/events/ghost_role/changeling_event.dm b/code/modules/events/ghost_role/changeling_event.dm index 43b4ca48af57d..ce34aaa07fa95 100644 --- a/code/modules/events/ghost_role/changeling_event.dm +++ b/code/modules/events/ghost_role/changeling_event.dm @@ -21,12 +21,9 @@ fakeable = FALSE /datum/round_event/ghost_role/changeling/spawn_role() - var/list/mob/dead/observer/candidate = SSpolling.poll_ghost_candidates(check_jobban = ROLE_CHANGELING, role = ROLE_CHANGELING_MIDROUND, pic_source = /obj/item/melee/arm_blade, role_name_text = role_name) - - if(!candidate.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_CHANGELING, role = ROLE_CHANGELING_MIDROUND, alert_pic = /obj/item/melee/arm_blade, role_name_text = role_name, amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - spawned_mobs += generate_changeling_meteor(pick_n_take(candidate)) - + spawned_mobs += generate_changeling_meteor(chosen_one) if(spawned_mobs) return SUCCESSFUL_SPAWN diff --git a/code/modules/events/ghost_role/fugitive_event.dm b/code/modules/events/ghost_role/fugitive_event.dm index 4b86e751c0b98..a09838ce473fb 100644 --- a/code/modules/events/ghost_role/fugitive_event.dm +++ b/code/modules/events/ghost_role/fugitive_event.dm @@ -20,7 +20,7 @@ if(isnull(landing_turf)) return MAP_ERROR var/list/possible_backstories = list() - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_FUGITIVE, role = ROLE_FUGITIVE, pic_source = /obj/item/card/id/advanced/prisoner) + var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_FUGITIVE, role = ROLE_FUGITIVE, alert_pic = /obj/item/card/id/advanced/prisoner, jump_target = landing_turf) if(!length(candidates)) return NOT_ENOUGH_PLAYERS @@ -111,7 +111,7 @@ addtimer(CALLBACK(src, PROC_REF(check_spawn_hunters), backstory, remaining_time - 1 MINUTES), 1 MINUTES) /datum/round_event/ghost_role/fugitives/proc/spawn_hunters(backstory) - var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a group of [backstory]?", check_jobban = ROLE_FUGITIVE_HUNTER, pic_source = /obj/machinery/sleeper, role_name_text = backstory) + var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for a group of [span_notice(backstory)]?", check_jobban = ROLE_FUGITIVE_HUNTER, alert_pic = /obj/machinery/sleeper, role_name_text = backstory) shuffle_inplace(candidates) var/datum/map_template/shuttle/hunter/ship diff --git a/code/modules/events/ghost_role/morph_event.dm b/code/modules/events/ghost_role/morph_event.dm index c9133863f8c0d..21d4b07873d86 100644 --- a/code/modules/events/ghost_role/morph_event.dm +++ b/code/modules/events/ghost_role/morph_event.dm @@ -13,13 +13,10 @@ role_name = "morphling" /datum/round_event/ghost_role/morph/spawn_role() - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /mob/living/basic/morph, role_name_text = "morph") - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, alert_pic = /mob/living/basic/morph, role_name_text = "morph", amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/selected = pick_n_take(candidates) - - var/datum/mind/player_mind = new /datum/mind(selected.key) + var/datum/mind/player_mind = new /datum/mind(chosen_one.key) player_mind.active = TRUE var/turf/spawn_loc = find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = FALSE) diff --git a/code/modules/events/ghost_role/nightmare.dm b/code/modules/events/ghost_role/nightmare.dm index ffb206c476dd1..d30108d94b984 100644 --- a/code/modules/events/ghost_role/nightmare.dm +++ b/code/modules/events/ghost_role/nightmare.dm @@ -15,13 +15,10 @@ fakeable = FALSE /datum/round_event/ghost_role/nightmare/spawn_role() - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_NIGHTMARE, role_name_text = role_name) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_NIGHTMARE, role_name_text = role_name, amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/selected = pick(candidates) - - var/datum/mind/player_mind = new /datum/mind(selected.key) + var/datum/mind/player_mind = new /datum/mind(chosen_one.key) player_mind.active = TRUE var/turf/spawn_loc = find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = TRUE) diff --git a/code/modules/events/ghost_role/operative.dm b/code/modules/events/ghost_role/operative.dm index fcea52e3c023b..98cfe5ecad41e 100644 --- a/code/modules/events/ghost_role/operative.dm +++ b/code/modules/events/ghost_role/operative.dm @@ -12,20 +12,16 @@ fakeable = FALSE /datum/round_event/ghost_role/operative/spawn_role() - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_OPERATIVE, role = ROLE_LONE_OPERATIVE, pic_source = /obj/machinery/nuclearbomb) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_OPERATIVE, role = ROLE_LONE_OPERATIVE, alert_pic = /obj/machinery/nuclearbomb, amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/selected = pick_n_take(candidates) - var/spawn_location = find_space_spawn() if(isnull(spawn_location)) return MAP_ERROR - var/mob/living/carbon/human/operative = new(spawn_location) operative.randomize_human_appearance(~RANDOMIZE_SPECIES) operative.dna.update_dna_identity() - var/datum/mind/Mind = new /datum/mind(selected.key) + var/datum/mind/Mind = new /datum/mind(chosen_one.key) Mind.set_assigned_role(SSjob.GetJobType(/datum/job/lone_operative)) Mind.special_role = ROLE_LONE_OPERATIVE Mind.active = TRUE diff --git a/code/modules/events/ghost_role/revenant_event.dm b/code/modules/events/ghost_role/revenant_event.dm index 6cdfc2c4c9e5a..7af53b847c86d 100644 --- a/code/modules/events/ghost_role/revenant_event.dm +++ b/code/modules/events/ghost_role/revenant_event.dm @@ -30,14 +30,10 @@ message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.") return WAITING_FOR_SOMETHING - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_REVENANT, role = ROLE_REVENANT, pic_source = /mob/living/basic/revenant) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_REVENANT, role = ROLE_REVENANT, alert_pic = /mob/living/basic/revenant, amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/observer/selected = pick_n_take(candidates) - var/list/spawn_locs = list() - for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies var/turf/T = get_turf(L) if(T && is_station_level(T.z)) @@ -50,16 +46,16 @@ if(!spawn_locs.len) //If we can't find any valid spawnpoints, try the carp spawns spawn_locs += find_space_spawn() if(!spawn_locs.len) //If we can't find either, just spawn the revenant at the player's location - spawn_locs += get_turf(selected) + spawn_locs += get_turf(chosen_one) if(!spawn_locs.len) //If we can't find THAT, then just give up and cry return MAP_ERROR var/mob/living/basic/revenant/revvie = new(pick(spawn_locs)) - revvie.key = selected.key + revvie.key = chosen_one.key message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by an event.") revvie.log_message("was spawned as a revenant by an event.", LOG_GAME) spawned_mobs += revvie - qdel(selected) + qdel(chosen_one) return SUCCESSFUL_SPAWN #undef REVENANT_SPAWN_THRESHOLD diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm index 8ebd30ad7b3e0..3aeebd298f43e 100644 --- a/code/modules/events/ghost_role/sentience.dm +++ b/code/modules/events/ghost_role/sentience.dm @@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /datum/round_event/ghost_role/sentience/spawn_role() var/list/mob/dead/observer/candidates - candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, pic_source = /obj/item/slimepotion/slime/sentience, role_name_text = role_name) + candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, alert_pic = /obj/item/slimepotion/slime/sentience, role_name_text = role_name) // find our chosen mob to breathe life into // Mobs have to be simple animals, mindless, on station, and NOT holograms. diff --git a/code/modules/events/ghost_role/sentient_disease.dm b/code/modules/events/ghost_role/sentient_disease.dm index 44f78d7c08cc7..156988d4b20d2 100644 --- a/code/modules/events/ghost_role/sentient_disease.dm +++ b/code/modules/events/ghost_role/sentient_disease.dm @@ -13,14 +13,11 @@ role_name = "sentient disease" /datum/round_event/ghost_role/sentient_disease/spawn_role() - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /obj/structure/sign/warning/biohazard, role_name_text = role_name) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, alert_pic = /obj/structure/sign/warning/biohazard, role_name_text = role_name, amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/observer/selected = pick_n_take(candidates) - var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center()) - virus.key = selected.key + virus.key = chosen_one.key INVOKE_ASYNC(virus, TYPE_PROC_REF(/mob/camera/disease, pick_name)) message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by an event.") virus.log_message("was spawned as a sentient disease by an event.", LOG_GAME) diff --git a/code/modules/events/ghost_role/slaughter_event.dm b/code/modules/events/ghost_role/slaughter_event.dm index f4628344d2f5b..2ea86551b799c 100644 --- a/code/modules/events/ghost_role/slaughter_event.dm +++ b/code/modules/events/ghost_role/slaughter_event.dm @@ -16,13 +16,10 @@ role_name = "slaughter demon" /datum/round_event/ghost_role/slaughter/spawn_role() - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, pic_source = /mob/living/basic/demon/slaughter, role_name_text = role_name) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, alert_pic = /mob/living/basic/demon/slaughter, role_name_text = role_name, amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/selected = pick_n_take(candidates) - - var/datum/mind/player_mind = new /datum/mind(selected.key) + var/datum/mind/player_mind = new /datum/mind(chosen_one.key) player_mind.active = TRUE var/spawn_location = find_space_spawn() diff --git a/code/modules/events/ghost_role/space_dragon.dm b/code/modules/events/ghost_role/space_dragon.dm index 0a328f6dc8d8c..8a39d4a5daea5 100644 --- a/code/modules/events/ghost_role/space_dragon.dm +++ b/code/modules/events/ghost_role/space_dragon.dm @@ -19,20 +19,14 @@ priority_announce("A large organic energy flux has been recorded near [station_name()], please stand by.", "Lifesign Alert") /datum/round_event/ghost_role/space_dragon/spawn_role() - - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SPACE_DRAGON, role = ROLE_SPACE_DRAGON, pic_source = /mob/living/basic/space_dragon) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SPACE_DRAGON, role = ROLE_SPACE_DRAGON, alert_pic = /mob/living/basic/space_dragon, amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/selected = pick(candidates) - var/key = selected.key - var/spawn_location = find_space_spawn() if(isnull(spawn_location)) return MAP_ERROR - - var/mob/living/basic/space_dragon/dragon = new (spawn_location) - dragon.key = key + var/mob/living/basic/space_dragon/dragon = new(spawn_location) + dragon.key = chosen_one.key dragon.mind.add_antag_datum(/datum/antagonist/space_dragon) playsound(dragon, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1) message_admins("[ADMIN_LOOKUPFLW(dragon)] has been made into a Space Dragon by an event.") diff --git a/code/modules/events/ghost_role/space_ninja.dm b/code/modules/events/ghost_role/space_ninja.dm index ffa28e6e1c4f3..eaccbe3de39cb 100644 --- a/code/modules/events/ghost_role/space_ninja.dm +++ b/code/modules/events/ghost_role/space_ninja.dm @@ -19,16 +19,12 @@ return MAP_ERROR //selecting a candidate player - var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_NINJA, role = ROLE_NINJA, pic_source = /obj/item/energy_katana) - if(!candidates.len) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_NINJA, role = ROLE_NINJA, alert_pic = /obj/item/energy_katana, jump_target = spawn_location, role_name_text = "space ninja", amount_to_pick = 1) + if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS - - var/mob/dead/selected_candidate = pick(candidates) - var/key = selected_candidate.key - //spawn the ninja and assign the candidate var/mob/living/carbon/human/ninja = create_space_ninja(spawn_location) - ninja.key = key + ninja.key = chosen_one.key ninja.mind.add_antag_datum(/datum/antagonist/ninja) spawned_mobs += ninja message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a space ninja by an event.") diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm index 7eb89e84bc06f..2d8a3bc549694 100644 --- a/code/modules/events/holiday/vday.dm +++ b/code/modules/events/holiday/vday.dm @@ -79,14 +79,15 @@ poll_time = 30 SECONDS, flash_window = FALSE, start_signed_up = TRUE, - pic_source = /obj/item/storage/fancy/heart_box, + alert_pic = /obj/item/storage/fancy/heart_box, custom_response_messages = list( POLL_RESPONSE_SIGNUP = "You have signed up for a date!", POLL_RESPONSE_ALREADY_SIGNED = "You are already signed up for a date.", POLL_RESPONSE_NOT_SIGNED = "You aren't signed up for a date.", POLL_RESPONSE_TOO_LATE_TO_UNREGISTER = "It's too late to decide against going on a date.", - POLL_RESPONSE_UNREGISTERED = "You deicde against going on a date.", + POLL_RESPONSE_UNREGISTERED = "You decide against going on a date.", ), + chat_text_border_icon = /obj/item/storage/fancy/heart_box, ) for(var/mob/living/second_check as anything in candidates_pruned) diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm index cad343f7debd9..20c4af94abdc3 100644 --- a/code/modules/events/holiday/xmas.dm +++ b/code/modules/events/holiday/xmas.dm @@ -84,11 +84,9 @@ priority_announce("Santa is coming to town!", "Unknown Transmission") /datum/round_event/santa/start() - var/list/candidates = SSpolling.poll_ghost_candidates("Santa is coming to town! Do you want to be Santa?", poll_time = 15 SECONDS, pic_source = /obj/item/clothing/head/costume/santa, role_name_text = "santa") - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) + var/mob/chosen_one = SSpolling.poll_ghost_candidates("Santa is coming to town! Do you want to be [span_notice("Santa")]?", poll_time = 15 SECONDS, alert_pic = /obj/item/clothing/head/costume/santa, role_name_text = "santa", amount_to_pick = 1) + if(chosen_one) santa = new /mob/living/carbon/human(pick(GLOB.blobstart)) - santa.key = C.key - + santa.key = chosen_one.key var/datum/antagonist/santa/A = new santa.mind.add_antag_datum(A) diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index 829942261427e..c2fb5472bdd82 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -13,20 +13,17 @@ if(!ishuman(M.current)) continue var/mob/living/carbon/human/W = M.current - var/list/candidates = SSpolling.poll_ghost_candidates("Would you like to be an imposter wizard?", check_jobban = ROLE_WIZARD, pic_source = /obj/item/clothing/head/wizard, role_name_text = "imposter wizard") - if(!length(candidates)) + var/mob/chosen_one = SSpolling.poll_ghost_candidates("Would you like to be an [span_notice("imposter wizard")]?", check_jobban = ROLE_WIZARD, alert_pic = /obj/item/clothing/head/wizard, jump_target = W, role_name_text = "imposter wizard", amount_to_pick = 1) + if(isnull(chosen_one)) return //Sad Trombone - var/mob/dead/observer/C = pick(candidates) - new /obj/effect/particle_effect/fluid/smoke(W.loc) - var/mob/living/carbon/human/I = new /mob/living/carbon/human(W.loc) W.dna.transfer_identity(I, transfer_SE=1) I.real_name = I.dna.real_name I.name = I.dna.real_name I.updateappearance(mutcolor_update=1) I.domutcheck() - I.key = C.key + I.key = chosen_one.key var/datum/antagonist/wizard/master = M.has_antag_datum(/datum/antagonist/wizard) if(!master.wiz_team) master.create_wiz_team() diff --git a/code/modules/food_and_drinks/plate.dm b/code/modules/food_and_drinks/plate.dm index a0d24dec8dacb..1df1d7c24bb91 100644 --- a/code/modules/food_and_drinks/plate.dm +++ b/code/modules/food_and_drinks/plate.dm @@ -22,7 +22,7 @@ . = ..() if(fragile) - AddElement(/datum/element/shatters_when_thrown) + AddElement(/datum/element/can_shatter) /obj/item/plate/attackby(obj/item/I, mob/user, params) if(!IS_EDIBLE(I)) diff --git a/code/modules/forensics/forensics_helpers.dm b/code/modules/forensics/forensics_helpers.dm index d71d1ebe1539c..8cb7f72184210 100644 --- a/code/modules/forensics/forensics_helpers.dm +++ b/code/modules/forensics/forensics_helpers.dm @@ -1,5 +1,7 @@ /// Adds a list of fingerprints to the atom /atom/proc/add_fingerprint_list(list/fingerprints_to_add) //ASSOC LIST FINGERPRINT = FINGERPRINT + if (QDELETED(src)) + return if (isnull(fingerprints_to_add)) return if (forensics) @@ -10,7 +12,7 @@ /// Adds a single fingerprint to the atom /atom/proc/add_fingerprint(mob/suspect, ignoregloves = FALSE) //Set ignoregloves to add prints irrespective of the mob having gloves on. - if (QDELING(src)) + if (QDELETED(src)) return if (isnull(forensics)) forensics = new(src) @@ -19,6 +21,8 @@ /// Add a list of fibers to the atom /atom/proc/add_fiber_list(list/fibers_to_add) //ASSOC LIST FIBERTEXT = FIBERTEXT + if (QDELETED(src)) + return if (isnull(fibers_to_add)) return if (forensics) @@ -29,6 +33,8 @@ /// Adds a single fiber to the atom /atom/proc/add_fibers(mob/living/carbon/human/suspect) + if (QDELETED(src)) + return var/old = 0 if(suspect.gloves && istype(suspect.gloves, /obj/item/clothing)) var/obj/item/clothing/gloves/suspect_gloves = suspect.gloves @@ -47,6 +53,8 @@ /// Adds a list of hiddenprints to the atom /atom/proc/add_hiddenprint_list(list/hiddenprints_to_add) //NOTE: THIS IS FOR ADMINISTRATION FINGERPRINTS, YOU MUST CUSTOM SET THIS TO INCLUDE CKEY/REAL NAMES! CHECK FORENSICS.DM + if (QDELETED(src)) + return if (isnull(hiddenprints_to_add)) return if (forensics) @@ -57,6 +65,8 @@ /// Adds a single hiddenprint to the atom /atom/proc/add_hiddenprint(mob/suspect) + if (QDELETED(src)) + return if (isnull(forensics)) forensics = new(src) forensics.add_hiddenprint(suspect) @@ -67,6 +77,8 @@ return FALSE /obj/add_blood_DNA(list/blood_DNA_to_add) + if (QDELETED(src)) + return . = ..() if (isnull(blood_DNA_to_add)) return . @@ -98,6 +110,8 @@ return FALSE /mob/living/carbon/human/add_blood_DNA(list/blood_DNA_to_add, list/datum/disease/diseases) + if (QDELETED(src)) + return if(wear_suit) wear_suit.add_blood_DNA(blood_DNA_to_add) update_worn_oversuit() diff --git a/code/modules/hallucination/fake_alert.dm b/code/modules/hallucination/fake_alert.dm index 537f6e294909a..6e10daf73aa97 100644 --- a/code/modules/hallucination/fake_alert.dm +++ b/code/modules/hallucination/fake_alert.dm @@ -63,10 +63,6 @@ alert_category = ALERT_TOO_MUCH_CO2 alert_type = /atom/movable/screen/alert/too_much_co2 -/datum/hallucination/fake_alert/nutrition - alert_category = ALERT_NUTRITION - alert_type = list(/atom/movable/screen/alert/fat, /atom/movable/screen/alert/starving) - /datum/hallucination/fake_alert/gravity alert_category = ALERT_GRAVITY alert_type = /atom/movable/screen/alert/weightless diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index f41f607dabe12..24e5274f7fbfe 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -456,8 +456,8 @@ /datum/holiday/france/greet() return "Do you hear the people sing?" -/datum/holiday/hotdogday //I have plans for this. - name = "National Hot Dog Day" +/datum/holiday/hotdogday + name = HOTDOG_DAY begin_day = 17 begin_month = JULY diff --git a/code/modules/hydroponics/grown/cereals.dm b/code/modules/hydroponics/grown/cereals.dm index 2bcc2860458bb..744c0dc5b023c 100644 --- a/code/modules/hydroponics/grown/cereals.dm +++ b/code/modules/hydroponics/grown/cereals.dm @@ -25,6 +25,8 @@ grind_results = list(/datum/reagent/consumable/flour = 0) tastes = list("wheat" = 1) distill_reagent = /datum/reagent/consumable/ethanol/beer + slot_flags = ITEM_SLOT_MASK + worn_icon = 'icons/mob/clothing/head/hydroponics.dmi' // Oat /obj/item/seeds/wheat/oat @@ -93,6 +95,8 @@ grind_results = list(/datum/reagent/consumable/flour = 0, /datum/reagent/blood = 0) tastes = list("meatwheat" = 1) can_distill = FALSE + slot_flags = ITEM_SLOT_MASK + worn_icon = 'icons/mob/clothing/head/hydroponics.dmi' /obj/item/food/grown/meatwheat/attack_self(mob/living/user) user.visible_message(span_notice("[user] crushes [src] into meat."), span_notice("You crush [src] into something that resembles meat.")) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 19b8a17ec04d5..b10a784779d6e 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -114,9 +114,12 @@ /// String. If set to a non-empty one, it will be the key for the policy text value to show this role on spawn. var/policy_index = "" - ///RPG job names, for the memes + /// RPG job names, for the memes var/rpg_title + /// Alternate titles to register as pointing to this job. + var/list/alternate_titles + /// Does this job ignore human authority? var/ignore_human_authority = FALSE diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm index 2be7bba5154d4..26a43fa775192 100644 --- a/code/modules/jobs/job_types/cook.dm +++ b/code/modules/jobs/job_types/cook.dm @@ -46,6 +46,9 @@ ) rpg_title = "Tavern Chef" + alternate_titles = list( + JOB_CHEF, + ) job_flags = STATION_JOB_FLAGS /datum/job/cook/award_service(client/winner, award) diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index 35a3a218f7b1a..4fb52ec77a026 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -38,6 +38,12 @@ /obj/item/melee/baton/security/boomerang/loaded = 1 ) rpg_title = "Guard" + alternate_titles = list( + JOB_SECURITY_OFFICER_MEDICAL, + JOB_SECURITY_OFFICER_ENGINEERING, + JOB_SECURITY_OFFICER_SUPPLY, + JOB_SECURITY_OFFICER_SCIENCE, + ) job_flags = STATION_JOB_FLAGS diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm index 6a5d1b1d5c4e4..656c90d77bbda 100644 --- a/code/modules/library/bibles.dm +++ b/code/modules/library/bibles.dm @@ -77,30 +77,24 @@ GLOBAL_LIST_INIT(bibleitemstates, list( unique = TRUE /// Deity this bible is related to var/deity_name = "Space Jesus" - /// Component which catches bullets for us - var/datum/component/bullet_catcher /obj/item/book/bible/Initialize(mapload) . = ..() AddComponent(/datum/component/anti_magic, MAGIC_RESISTANCE_HOLY) - bullet_catcher = AddComponent(\ + AddComponent(\ /datum/component/bullet_intercepting,\ active_slots = ITEM_SLOT_SUITSTORE,\ on_intercepted = CALLBACK(src, PROC_REF(on_intercepted_bullet)),\ + block_charges = 1,\ ) - carve_out() - -/obj/item/book/bible/Destroy(force) - QDEL_NULL(bullet_catcher) - return ..() /// Destroy the bible when it's shot by a bullet /obj/item/book/bible/proc/on_intercepted_bullet(mob/living/victim, obj/projectile/bullet) victim.add_mood_event("blessing", /datum/mood_event/blessing) playsound(victim, 'sound/magic/magic_block_holy.ogg', 50, TRUE) - victim.visible_message(span_warning("\The [src] takes \the [bullet] in [victim]'s place!")) + victim.visible_message(span_warning("[src] takes [bullet] in [victim]'s place!")) var/obj/structure/fluff/paper/stack/pages = new(get_turf(src)) - pages.dir = pick(GLOB.alldirs) + pages.setDir(pick(GLOB.alldirs)) name = "punctured bible" desc = "A memento of good luck, or perhaps divine intervention?" icon_state = "shot" @@ -108,7 +102,6 @@ GLOBAL_LIST_INIT(bibleitemstates, list( GLOB.bible_icon_state = "shot" // New symbol of your religion if you hadn't picked one atom_storage?.remove_all(get_turf(src)) QDEL_NULL(atom_storage) - QDEL_NULL(bullet_catcher) /obj/item/book/bible/examine(mob/user) . = ..() @@ -345,6 +338,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list( /obj/item/book/bible/booze/Initialize(mapload) . = ..() + carve_out() new /obj/item/reagent_containers/cup/glass/bottle/whiskey(src) /obj/item/book/bible/syndicate diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm b/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm index 20210d56cc95c..8a2949f98935e 100644 --- a/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm +++ b/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm @@ -33,6 +33,10 @@ new_replica.name = "[appearance_object.name][obvious_replica ? " replica" : ""]" new_replica.desc = "[appearance_object.desc][obvious_replica ? " ..except this one is a replica.": ""]" + + new_replica.pixel_y = pixel_y + new_replica.pixel_x = pixel_x + qdel(appearance_object) qdel(src) return INITIALIZE_HINT_QDEL @@ -129,3 +133,68 @@
sometimes i can catch them moving

we should have never come here"} + +/obj/item/paper/fluff/museum/chefs_ultimatum + name = "old note" + default_raw_text = {"I messed it up big times. +
I broke the button and now I'm stuck. +
Anyway, I don't have the key on me. I flushed it down. +
Hell knows where it's now, shit's like all linked together here."} + +/obj/item/paper/fluff/museum/numbers_on_walls + name = "reprimanding note" + default_raw_text = "Please refraim from writing the pass all over the place. I know you've the memory of a goldfish, but, like, just put it on a piece of paper, no?" + +/obj/effect/mob_spawn/corpse/human/skeleton/museum_chef + name = "Dead Museum Cafeteria Chef" + mob_name = "Nameless Chef" + outfit = /datum/outfit/museum_chef + +/datum/outfit/museum_chef + name = "Dead Museum Cafeteria Chef" + uniform = /obj/item/clothing/under/color/green + suit = /obj/item/clothing/suit/toggle/chef + head = /obj/item/clothing/head/utility/chefhat + shoes = /obj/item/clothing/shoes/laceup + mask = /obj/item/clothing/mask/fakemoustache/italian + +/obj/machinery/vending/hotdog/museum + obj_flags = parent_type::obj_flags|NO_DECONSTRUCTION + onstation_override = TRUE + +#define CAFE_KEYCARD_TOILETS "museum_cafe_key_toilets" + +///Do not place these beyond the cafeteria shutters, or you might lock people out of reaching it. +/obj/structure/toilet/museum + +/obj/structure/toilet/museum/Initialize(mapload) + . = ..() + if(mapload) + SSqueuelinks.add_to_queue(src, CAFE_KEYCARD_TOILETS) + +/obj/item/keycard/cafeteria + name = "museum cafeteria keycard" + color = COLOR_OLIVE + puzzle_id = "museum_cafeteria" + desc = "The key to the cafeteria, as the name implies." + +/obj/item/keycard/cafeteria/Initialize(mapload) + . = ..() + if(mapload) + SSqueuelinks.add_to_queue(src, CAFE_KEYCARD_TOILETS) + return INITIALIZE_HINT_LATELOAD + +/obj/item/keycard/cafeteria/LateInitialize() + . = ..() + if(SSqueuelinks.queues[CAFE_KEYCARD_TOILETS]) + SSqueuelinks.pop_link(CAFE_KEYCARD_TOILETS) + +/obj/item/keycard/cafeteria/MatchedLinks(id, partners) + if(id != CAFE_KEYCARD_TOILETS) + return ..() + var/obj/structure/toilet/destination = pick(partners) + forceMove(destination) + destination.w_items += w_class + destination.contents += src + +#undef CAFE_KEYCARD_TOILETS diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm b/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm index 5647b5aca2382..0db718e399bf6 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm @@ -76,12 +76,12 @@ SEND_SIGNAL(src, COMSIG_PUZZLE_COMPLETED) qdel(src) -/obj/machinery/puzzle_button/meatderelict +/obj/machinery/puzzle/button/meatderelict name = "lockdown panel" desc = "A panel that controls the lockdown of this outpost." id = "md_prevault" -/obj/machinery/puzzle_button/meatderelict/open_doors() +/obj/machinery/puzzle/button/meatderelict/on_puzzle_complete() . = ..() playsound(src, 'sound/effects/alert.ogg', 100, TRUE) visible_message(span_warning("[src] lets out an alarm as the lockdown is lifted!")) @@ -121,7 +121,7 @@ /obj/lightning_thrower/Initialize(mapload) . = ..() - START_PROCESSING(SSprocessing, src) + START_PROCESSING(SSprocessing, src) /obj/lightning_thrower/Destroy() . = ..() diff --git a/code/modules/mining/boulder_processing/_boulder_processing.dm b/code/modules/mining/boulder_processing/_boulder_processing.dm index d7d4be2557e10..b79bfe36dc6d3 100644 --- a/code/modules/mining/boulder_processing/_boulder_processing.dm +++ b/code/modules/mining/boulder_processing/_boulder_processing.dm @@ -66,9 +66,9 @@ else if(istype(held_item, /obj/item/card/id) && points_held > 0) context[SCREENTIP_CONTEXT_LMB] = "Claim mining points" else if(held_item.tool_behaviour == TOOL_SCREWDRIVER) - context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] Panel" + context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] panel" else if(held_item.tool_behaviour == TOOL_WRENCH) - context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "" : "Un"] Anchor" + context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]Anchor" else if(panel_open && held_item.tool_behaviour == TOOL_CROWBAR) context[SCREENTIP_CONTEXT_LMB] = "Deconstruct" @@ -96,37 +96,17 @@ /obj/machinery/bouldertech/update_icon_state() . = ..() var/suffix = "" - if(!anchored || !is_operational || (machine_stat & (BROKEN | NOPOWER)) || panel_open) + if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER))) suffix = "-off" icon_state ="[initial(icon_state)][suffix]" -/obj/machinery/bouldertech/wrench_act(mob/living/user, obj/item/tool) - . = ITEM_INTERACT_BLOCKING - if(default_unfasten_wrench(user, tool, time = 1.5 SECONDS) == SUCCESSFUL_UNFASTEN) - if(anchored) - begin_processing() - else - end_processing() - update_appearance(UPDATE_ICON_STATE) - return ITEM_INTERACT_SUCCESS - -/obj/machinery/bouldertech/screwdriver_act(mob/living/user, obj/item/tool) - . = ITEM_INTERACT_BLOCKING - if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-off", initial(icon_state), tool)) - update_appearance(UPDATE_ICON_STATE) - return ITEM_INTERACT_SUCCESS - -/obj/machinery/bouldertech/crowbar_act(mob/living/user, obj/item/tool) - . = ITEM_INTERACT_BLOCKING - if(default_deconstruction_crowbar(tool)) - return ITEM_INTERACT_SUCCESS - /obj/machinery/bouldertech/CanAllowThrough(atom/movable/mover, border_dir) if(!anchored) return FALSE if(istype(mover, /obj/item/boulder)) - var/obj/item/boulder/boulder = mover - return can_process_boulder(boulder) + return can_process_boulder(mover) + if(isgolem(mover)) + return can_process_golem(mover) return ..() /** @@ -140,7 +120,7 @@ SHOULD_BE_PURE(TRUE) //machine not operational - if(!anchored || panel_open || !is_operational || machine_stat & (BROKEN | NOPOWER)) + if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER))) return FALSE //not a valid boulder @@ -168,6 +148,8 @@ * * obj/item/boulder/new_boulder - the boulder to accept */ /obj/machinery/bouldertech/proc/accept_boulder(obj/item/boulder/new_boulder) + PRIVATE_PROC(TRUE) + if(!can_process_boulder(new_boulder)) return FALSE @@ -177,13 +159,67 @@ return TRUE +/** + * Can we maim this golem + * Arguments + * + * * [rockman][mob/living/carbon/human] - the golem we are trying to main + */ +/obj/machinery/bouldertech/proc/can_process_golem(mob/living/carbon/human/rockman) + PRIVATE_PROC(TRUE) + SHOULD_BE_PURE(TRUE) + + //not operatinal + if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER))) + return FALSE + + //still in cooldown + if(!COOLDOWN_FINISHED(src, accept_cooldown)) + return FALSE + + //not processable + if(!istype(rockman) || QDELETED(rockman) || rockman.body_position != LYING_DOWN) + return FALSE + + return TRUE + +/** + * Accepts a golem to be processed, mainly for memes + * Arguments + * + * * [rockman][mob/living/carbon/human] - the golem we are trying to main + */ +/obj/machinery/bouldertech/proc/accept_golem(mob/living/carbon/human/rockman) + PRIVATE_PROC(TRUE) + + if(!can_process_golem(rockman)) + return + + maim_golem(rockman) + use_power(active_power_usage * 1.5) + 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) + PROTECTED_PROC(TRUE) + + Shake(duration = 1 SECONDS) + 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.gib(DROP_ALL_REMAINS) + /obj/machinery/bouldertech/proc/on_entered(datum/source, atom/movable/atom_movable) SIGNAL_HANDLER - if(!can_process_boulder(atom_movable)) + if(istype(atom_movable, /obj/item/boulder)) + INVOKE_ASYNC(src, PROC_REF(accept_boulder), atom_movable) return - INVOKE_ASYNC(src, PROC_REF(accept_boulder), atom_movable) + if(isgolem(atom_movable)) + INVOKE_ASYNC(src, PROC_REF(accept_golem), atom_movable) + return /** * Looks for a boost to the machine's efficiency, and applies it if found. @@ -205,42 +241,61 @@ return FALSE -/obj/machinery/bouldertech/attackby(obj/item/attacking_item, mob/user, params) - if(panel_open) +/obj/machinery/bouldertech/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) + if(panel_open || user.combat_mode) return ..() - if(istype(attacking_item, /obj/item/boulder)) - . = TRUE - var/obj/item/boulder/my_boulder = attacking_item + if(istype(tool, /obj/item/boulder)) + var/obj/item/boulder/my_boulder = tool if(!accept_boulder(my_boulder)) balloon_alert_to_viewers("cannot accept!") - return + return ITEM_INTERACT_BLOCKING balloon_alert_to_viewers("accepted") - return + return ITEM_INTERACT_SUCCESS - if(istype(attacking_item, /obj/item/card/id)) - . = TRUE + if(istype(tool, /obj/item/card/id)) if(points_held <= 0) balloon_alert_to_viewers("no points to claim!") if(!COOLDOWN_FINISHED(src, sound_cooldown)) - return + return ITEM_INTERACT_BLOCKING COOLDOWN_START(src, sound_cooldown, 1.5 SECONDS) playsound(src, 'sound/machines/buzz-sigh.ogg', 30, FALSE) - return + return ITEM_INTERACT_BLOCKING - var/obj/item/card/id/id_card = attacking_item + var/obj/item/card/id/id_card = tool var/amount = tgui_input_number(user, "How many mining points do you wish to claim? ID Balance: [id_card.registered_account.mining_points], stored mining points: [points_held]", "Transfer Points", max_value = points_held, min_value = 0, round_value = 1) if(!amount) - return + return ITEM_INTERACT_BLOCKING if(amount > points_held) amount = points_held id_card.registered_account.mining_points += amount points_held = round(points_held - amount) to_chat(user, span_notice("You claim [amount] mining points from \the [src] to [id_card].")) - return + return ITEM_INTERACT_SUCCESS return ..() +/obj/machinery/bouldertech/wrench_act(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if(default_unfasten_wrench(user, tool, time = 1.5 SECONDS) == SUCCESSFUL_UNFASTEN) + if(anchored) + begin_processing() + else + end_processing() + update_appearance(UPDATE_ICON_STATE) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/bouldertech/screwdriver_act(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-off", initial(icon_state), tool)) + update_appearance(UPDATE_ICON_STATE) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/bouldertech/crowbar_act(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if(default_deconstruction_crowbar(tool)) + return ITEM_INTERACT_SUCCESS + /obj/machinery/bouldertech/attack_hand_secondary(mob/user, list/modifiers) . = ..() if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open) @@ -266,15 +321,17 @@ * Accepts a boulder into the machinery, then converts it into minerals. * If the boulder can be fully processed by this machine, we take the materials, insert it into the silo, and destroy the boulder. * If the boulder has materials left, we make a copy of the boulder to hold the processable materials, take the processable parts, and eject the original boulder. - * @param chosen_boulder The boulder to being breaking down into minerals. + * Arguments + * + * * obj/item/boulder/chosen_boulder - The boulder to being breaking down into minerals. */ /obj/machinery/bouldertech/proc/breakdown_boulder(obj/item/boulder/chosen_boulder) PRIVATE_PROC(TRUE) if(QDELETED(chosen_boulder)) - return FALSE + return if(chosen_boulder.loc != src) - return FALSE + return //if boulders are kept inside because there is no space to eject them, then they could be reprocessed, lets avoid that if(!chosen_boulder.processed_by) @@ -301,7 +358,7 @@ if(istype(chosen_boulder, /obj/item/boulder/artifact)) points_held = round((points_held + MINER_POINT_MULTIPLIER) * MINING_POINT_MACHINE_MULTIPLIER) /// Artifacts give bonus points! chosen_boulder.break_apart() - return TRUE //We've processed all the materials in the boulder, so we can just destroy it in break_apart. + return//We've processed all the materials in the boulder, so we can just destroy it in break_apart. chosen_boulder.processed_by = src @@ -309,7 +366,7 @@ remove_boulder(chosen_boulder) /obj/machinery/bouldertech/process() - if(!anchored || panel_open || !is_operational || machine_stat & (BROKEN | NOPOWER)) + if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER))) return var/boulders_found = FALSE diff --git a/code/modules/mining/boulder_processing/brm.dm b/code/modules/mining/boulder_processing/brm.dm index e4d525bff056c..592ade5b75796 100644 --- a/code/modules/mining/boulder_processing/brm.dm +++ b/code/modules/mining/boulder_processing/brm.dm @@ -37,26 +37,23 @@ register_context() /obj/machinery/brm/add_context(atom/source, list/context, obj/item/held_item, mob/user) - . = CONTEXTUAL_SCREENTIP_SET + . = NONE if(isnull(held_item)) context[SCREENTIP_CONTEXT_LMB] = "Teleport single boulder" context[SCREENTIP_CONTEXT_RMB] = "Toggle [toggled_on ? "Off" : "On"] automatic boulder retrieval" - return + return CONTEXTUAL_SCREENTIP_SET if(!isnull(held_item)) if(held_item.tool_behaviour == TOOL_WRENCH) - context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "" : "Un"] Anchor" - return - if(held_item.tool_behaviour == TOOL_SCREWDRIVER) - context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] Panel" - return - - if(panel_open) - if(held_item.tool_behaviour == TOOL_CROWBAR) - context[SCREENTIP_CONTEXT_LMB] = "Deconstruct" - - return CONTEXTUAL_SCREENTIP_SET + context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]Anchor" + return CONTEXTUAL_SCREENTIP_SET + else if(held_item.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] panel" + return CONTEXTUAL_SCREENTIP_SET + else if(panel_open && held_item.tool_behaviour == TOOL_CROWBAR) + context[SCREENTIP_CONTEXT_LMB] = "Deconstruct" + return CONTEXTUAL_SCREENTIP_SET /obj/machinery/brm/examine(mob/user) . = ..() @@ -131,8 +128,6 @@ var/result = pre_collect_boulder() if(result == TURF_BLOCKED_BY_BOULDER) balloon_alert(user, "no space") - else if(result) - balloon_alert(user, "teleporting") COOLDOWN_START(src, manual_teleport_cooldown, TELEPORTATION_TIME) return TRUE diff --git a/code/modules/mining/boulder_processing/refinery.dm b/code/modules/mining/boulder_processing/refinery.dm index 662bb3e75e1e5..a751566efa107 100644 --- a/code/modules/mining/boulder_processing/refinery.dm +++ b/code/modules/mining/boulder_processing/refinery.dm @@ -85,3 +85,7 @@ /obj/machinery/bouldertech/refinery/smelter/on_set_is_operational(old_value) set_light_on(TRUE) +/obj/machinery/bouldertech/refinery/smelter/maim_golem(mob/living/carbon/human/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.dust() diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index e1df98239e2cd..56ea43b0c6773 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -470,3 +470,17 @@ continue return possible_turf return get_turf(user) + +//wolf trophy + +/obj/item/crusher_trophy/wolf_ear + name = "wolf ear" + desc = "It's a wolf ear." + icon_state = "wolf_ear" + denied_type = /obj/item/crusher_trophy/wolf_ear + +/obj/item/crusher_trophy/wolf_ear/effect_desc() + return "mark detonation to gain a slight speed boost temporarily" + +/obj/item/crusher_trophy/wolf_ear/on_mark_detonation(mob/living/target, mob/living/user) + user.apply_status_effect(/datum/status_effect/speed_boost, 1 SECONDS) diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm index 45c62ebd0a0d8..25240b0330fb3 100644 --- a/code/modules/mining/lavaland/megafauna_loot.dm +++ b/code/modules/mining/lavaland/megafauna_loot.dm @@ -422,13 +422,16 @@ using = TRUE balloon_alert(user, "you hold the scythe up...") ADD_TRAIT(src, TRAIT_NODROP, type) - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user) - AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_POSSESSED_BLADE, \ - job_bans = ROLE_PAI, \ - to_call = to_call, \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + check_jobban = ROLE_PAI, + poll_time = 20 SECONDS, + checked_target = src, + ignore_category = POLL_IGNORE_POSSESSED_BLADE, + alert_pic = src, + role_name_text = "soulscythe soul", + chat_text_border_icon = src, ) + on_poll_concluded(user, chosen_one) /// Ghost poll has concluded and a candidate has been chosen. /obj/item/soulscythe/proc/on_poll_concluded(mob/living/master, mob/dead/observer/ghost) diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 62458347bf013..11a941c409861 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -147,21 +147,10 @@ /obj/machinery/mineral/processing_unit/Initialize(mapload) . = ..() proximity_monitor = new(src, 1) - var/list/allowed_materials = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/silver, - /datum/material/gold, - /datum/material/diamond, - /datum/material/plasma, - /datum/material/uranium, - /datum/material/bananium, - /datum/material/titanium, - /datum/material/bluespace, - ) + materials = AddComponent( \ /datum/component/material_container, \ - allowed_materials, \ + SSmaterials.materials_by_category[MAT_CATEGORY_SILO], \ INFINITY, \ MATCONTAINER_EXAMINE, \ allowed_items = /obj/item/stack \ diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm index 9320bc012264c..97c3a90b78eb1 100644 --- a/code/modules/mining/machine_silo.dm +++ b/code/modules/mining/machine_silo.dm @@ -18,22 +18,9 @@ /obj/machinery/ore_silo/Initialize(mapload) . = ..() - var/static/list/materials_list = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/silver, - /datum/material/gold, - /datum/material/diamond, - /datum/material/plasma, - /datum/material/uranium, - /datum/material/bananium, - /datum/material/titanium, - /datum/material/bluespace, - /datum/material/plastic, - ) materials = AddComponent( \ /datum/component/material_container, \ - materials_list, \ + SSmaterials.materials_by_category[MAT_CATEGORY_SILO], \ INFINITY, \ MATCONTAINER_EXAMINE, \ container_signals = list( \ diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 0b499f590d668..4fe26281b10a7 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -614,7 +614,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ /obj/item/coin/eldritch name = "eldritch coin" - desc = "Everytime it lands it bolts or opens doors, except for you." + desc = "A surprisingly heavy, ornate coin. Its sides seem to depict a different image each time you look." icon_state = "coin_heretic" custom_materials = list(/datum/material/diamond =HALF_SHEET_MATERIAL_AMOUNT, /datum/material/plasma =HALF_SHEET_MATERIAL_AMOUNT) sideslist = list("heretic", "blade") diff --git a/code/modules/mob/dead/observer/observer_movement.dm b/code/modules/mob/dead/observer/observer_movement.dm index 9e156913a98a7..6972d3b265ff6 100644 --- a/code/modules/mob/dead/observer/observer_movement.dm +++ b/code/modules/mob/dead/observer/observer_movement.dm @@ -1,11 +1,17 @@ +/mob/dead/observer/down() + set name = "Move Down" + set category = "IC" + + if(zMove(DOWN, z_move_flags = ZMOVE_FEEDBACK)) + to_chat(src, span_notice("You move down.")) + /mob/dead/observer/up() set name = "Move Upwards" set category = "IC" if(zMove(UP, z_move_flags = ZMOVE_FEEDBACK)) - to_chat(src, "You move upwards.") + to_chat(src, span_notice("You move upwards.")) /mob/dead/observer/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) z_move_flags |= ZMOVE_IGNORE_OBSTACLES //observers do not respect these FLOORS you speak so much of. return ..() - diff --git a/code/modules/mob/dead/observer/observer_say.dm b/code/modules/mob/dead/observer/observer_say.dm index e63839b123ff4..e43086349b34e 100644 --- a/code/modules/mob/dead/observer/observer_say.dm +++ b/code/modules/mob/dead/observer/observer_say.dm @@ -9,7 +9,19 @@ mods[RADIO_EXTENSION] = GLOB.department_radio_keys[mods[RADIO_KEY]] return message -/mob/dead/observer/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/dead/observer/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) message = trim(message) //trim now and sanitize after checking for special admin radio keys var/list/filter_result = CAN_BYPASS_FILTER(src) ? null : is_ooc_filtered(message) @@ -27,7 +39,6 @@ if(!message) return - var/list/message_mods = list() message = get_message_mods(message, message_mods) if(client?.holder && (message_mods[RADIO_EXTENSION] == MODE_ADMIN || message_mods[RADIO_EXTENSION] == MODE_DEADMIN || (message_mods[RADIO_EXTENSION] == MODE_PUPPET && mind?.current))) message = trim_left(copytext_char(message, length(message_mods[RADIO_KEY]) + 2)) diff --git a/code/modules/mob/living/basic/guardian/guardian_creator.dm b/code/modules/mob/living/basic/guardian/guardian_creator.dm index 3f1f092752217..441a60124a7bf 100644 --- a/code/modules/mob/living/basic/guardian/guardian_creator.dm +++ b/code/modules/mob/living/basic/guardian/guardian_creator.dm @@ -87,17 +87,18 @@ GLOBAL_LIST_INIT(guardian_radial_images, setup_guardian_radial()) used = TRUE to_chat(user, use_message) var/guardian_type_name = random ? "Random" : capitalize(initial(guardian_path.creator_name)) - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates( - "Do you want to play as [user.real_name]'s [guardian_type_name] [mob_name]?", + var/mob/chosen_one = SSpolling.poll_ghost_candidates( + "Do you want to play as [span_danger("[user.real_name]'s")] [span_notice("[guardian_type_name] [mob_name]")]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_HOLOPARASITE, - pic_source = src, - role_name_text = "guardian spirit", + alert_pic = guardian_path, + jump_target = src, + role_name_text = guardian_type_name, + amount_to_pick = 1, ) - if(LAZYLEN(candidates)) - var/mob/dead/observer/candidate = pick(candidates) - spawn_guardian(user, candidate, guardian_path) + if(chosen_one) + spawn_guardian(user, chosen_one, guardian_path) used = TRUE SEND_SIGNAL(src, COMSIG_TRAITOR_ITEM_USED(type)) else diff --git a/code/modules/mob/living/basic/guardian/guardian_verbs.dm b/code/modules/mob/living/basic/guardian/guardian_verbs.dm index 2f40da369f82a..80a2af7db7a27 100644 --- a/code/modules/mob/living/basic/guardian/guardian_verbs.dm +++ b/code/modules/mob/living/basic/guardian/guardian_verbs.dm @@ -169,20 +169,18 @@ return FALSE to_chat(owner, span_holoparasite("You attempt to reset [span_bold(chosen_guardian.real_name)]'s personality...")) - var/list/mob/dead/observer/ghost_candidates = SSpolling.poll_ghost_candidates("Do you want to play as [owner.real_name]'s [chosen_guardian.theme.name]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, pic_source = chosen_guardian, role_name_text = chosen_guardian.theme.name) - if (!LAZYLEN(ghost_candidates)) + var/mob/chosen_one = SSpolling.poll_ghost_candidates("Do you want to play as [span_danger("[owner.real_name]'s")] [span_notice(chosen_guardian.theme.name)]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, alert_pic = chosen_guardian, jump_target = owner, role_name_text = chosen_guardian.theme.name, amount_to_pick = 1) + if(isnull(chosen_one)) to_chat(owner, span_holoparasite("Your attempt to reset the personality of \ [span_bold(chosen_guardian.real_name)] appears to have failed... \ Looks like you're stuck with it for now.")) StartCooldown() return FALSE - - var/mob/dead/observer/candidate = pick(ghost_candidates) to_chat(chosen_guardian, span_holoparasite("Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.")) to_chat(owner, span_boldholoparasite("The personality of [chosen_guardian.theme.name] has been successfully reset.")) - message_admins("[key_name_admin(candidate)] has taken control of ([ADMIN_LOOKUPFLW(chosen_guardian)])") + message_admins("[key_name_admin(chosen_one)] has taken control of ([ADMIN_LOOKUPFLW(chosen_guardian)])") chosen_guardian.ghostize(FALSE) - chosen_guardian.key = candidate.key + chosen_guardian.key = chosen_one.key COOLDOWN_START(chosen_guardian, resetting_cooldown, 5 MINUTES) chosen_guardian.guardian_rename() //give it a new color and name, to show it's a new person chosen_guardian.guardian_recolour() diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index f139a43d988df..cba3dd6e588e3 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -197,6 +197,7 @@ butcher_results = list(/obj/item/ectoplasm = 1) ai_controller = /datum/ai_controller/basic_controller/parrot/ghost speech_probability_rate = 1 + resistance_flags = parent_type::resistance_flags | SHUTTLE_CRUSH_PROOF /mob/living/basic/parrot/poly/ghost/Initialize(mapload) // block anything and everything that could possibly happen with writing memory for ghosts diff --git a/code/modules/mob/living/basic/ruin_defender/flesh.dm b/code/modules/mob/living/basic/ruin_defender/flesh.dm index 38e56f84c845e..e33cdcad1a1ea 100644 --- a/code/modules/mob/living/basic/ruin_defender/flesh.dm +++ b/code/modules/mob/living/basic/ruin_defender/flesh.dm @@ -49,12 +49,12 @@ if(isnull(current_bodypart) || isnull(current_bodypart.owner)) return var/mob/living/carbon/human/victim = current_bodypart.owner - if(prob(SPT_PROB(3, SSMOBS_DT))) + if(SPT_PROB(3, SSMOBS_DT)) to_chat(victim, span_warning("The thing posing as your limb makes you feel funny...")) //warn em //firstly as a sideeffect we drain nutrition from our host victim.adjust_nutrition(-1.5) - if(!prob(SPT_PROB(1.5, SSMOBS_DT))) + if(!SPT_PROB(1.5, SSMOBS_DT)) return if(istype(current_bodypart, /obj/item/bodypart/arm)) diff --git a/code/modules/mob/living/basic/space_fauna/bear/_bear.dm b/code/modules/mob/living/basic/space_fauna/bear/_bear.dm index e23e022d00e61..eef08b30197d1 100644 --- a/code/modules/mob/living/basic/space_fauna/bear/_bear.dm +++ b/code/modules/mob/living/basic/space_fauna/bear/_bear.dm @@ -82,6 +82,12 @@ icon_dead = "snowbear_dead" desc = "It's a polar bear, in space, but not actually in space." +/mob/living/basic/bear/snow/ancient + name = "ancient polar bear" + desc = "A grizzled old polar bear, its hide thick enough to make it impervious to almost all weapons." + status_flags = CANPUSH | GODMODE + gold_core_spawnable = NO_SPAWN + /mob/living/basic/bear/snow/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_SNOWSTORM_IMMUNE, INNATE_TRAIT) 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 1be2a870de3ba..0ee7d16615ffc 100644 --- a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm +++ b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm @@ -1,6 +1,6 @@ /mob/living/basic/migo name = "mi-go" - desc = "A pinkish, fungoid crustacean-like creature with numerous pairs of clawed appendages and a head covered with waving antennae." + desc = "A pinkish, fungoid crustacean-like creature with clawed appendages and a head covered with waving antennae." icon_state = "mi-go" icon_living = "mi-go" icon_dead = "mi-go-dead" @@ -36,6 +36,10 @@ /mob/living/basic/migo/Initialize(mapload) . = ..() migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/voice/beepsky/creep.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/medbot/patchedup.ogg', 'sound/voice/medbot/feelbetter.ogg', 'sound/voice/human/manlaugh1.ogg', 'sound/voice/human/womanlaugh.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/clockcultalr.ogg', 'sound/ambience/antag/ling_alert.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/ambience/antag/monkey.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/footstep/clownstep2.ogg', 'sound/effects/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion_distant.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/runtime/hyperspace/hyperspace_begin.ogg', 'sound/runtime/hyperspace/hyperspace_end.ogg', 'sound/effects/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desecration-01.ogg', 'sound/misc/desecration-02.ogg', 'sound/misc/desecration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/misc/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/beam_sniper.ogg', 'sound/weapons/chainsawhit.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gun/pistol/shot_suppressed.ogg', 'sound/weapons/gun/pistol/shot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kinetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlockclose.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlockopen.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/triple_beep.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/default/outbreak5.ogg', 'sound/ai/default/outbreak7.ogg', 'sound/ai/default/poweroff.ogg', 'sound/ai/default/radiation.ogg', 'sound/ai/default/shuttlecalled.ogg', 'sound/ai/default/shuttledock.ogg', 'sound/ai/default/shuttlerecalled.ogg', 'sound/ai/default/aimalf.ogg') //hahahaha fuck you code divers + if(prob(0.04)) //1 in 20 chance on-load mi-gos will spawn with a miku wig on. + base_icon_state = "mi-go-h" + name = "hatsune mi-go" + desc += " This one is wearing a bright blue wig." AddElement(/datum/element/swabable, CELL_LINE_TABLE_NETHER, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 0) AddComponent(/datum/component/health_scaling_effects, min_health_slowdown = -1.5, additional_status_callback = CALLBACK(src, PROC_REF(update_dodge_chance))) @@ -43,12 +47,11 @@ /mob/living/basic/migo/proc/update_dodge_chance(health_ratio) dodge_prob = LERP(50, 10, health_ratio) -/mob/living/basic/migo/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - ..() - if(stat) +/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) return - var/chosen_sound = pick(migo_sounds) - playsound(src, chosen_sound, 50, TRUE) + playsound(src, pick(migo_sounds), 50, TRUE) /mob/living/basic/migo/Life(seconds_per_tick = SSMOBS_DT, times_fired) ..() @@ -71,3 +74,10 @@ . = Move(get_step(loc,pick(cdir, ccdir))) if(!.)//Can't dodge there so we just carry on . = Move(moving_to, move_direction) + +/mob/living/basic/migo/hatsune //Admin-spawnable variant of the miku mi-go. + name = "hatsune mi-go" + desc = "A pinkish, fungoid crustacean-like creature with clawed appendages and a head covered with waving antennae. This one is wearing a bright blue wig." + icon_state = "mi-go-h" + icon_living = "mi-go-h" + gold_core_spawnable = NO_SPAWN 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 be83d3e058f91..51379ce88a0bc 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 @@ -62,6 +62,7 @@ poll_ignore_key = POLL_IGNORE_REGAL_RAT,\ assumed_control_message = "You are an independent, invasive force on the station! Hoard coins, trash, cheese, and the like from the safety of darkness!",\ after_assumed_control = CALLBACK(src, PROC_REF(became_player_controlled)),\ + poll_chat_border_icon = /obj/item/food/cheese/wedge,\ ) var/static/list/innate_actions = list( 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 5a901fa2c79cb..0a5239e1f6e23 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm @@ -168,7 +168,19 @@ essencecolor = "#1D2953" //oh jeez you're dying hud_used.healths.maptext = MAPTEXT("
[essence]E
") -/mob/living/basic/revenant/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/basic/revenant/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!message) return 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 3ea62afd9f80d..40bad3b7981b9 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 @@ -206,6 +206,12 @@ bot.bot_cover_flags &= ~BOT_COVER_LOCKED bot.bot_cover_flags |= BOT_COVER_OPEN bot.emag_act(caster) + for(var/mob/living/basic/bot/bot in victim) + if(!(bot.bot_access_flags & BOT_COVER_EMAGGED)) + new /obj/effect/temp_visual/revenant(bot.loc) + bot.bot_access_flags |= BOT_CONTROL_PANEL_OPEN + bot.bot_access_flags |= BOT_MAINTS_PANEL_OPEN + bot.emag_act(caster) for(var/mob/living/carbon/human/human in victim) if(human == caster) continue diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm index ea153b03c063c..f8a3db0202a29 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_items.dm @@ -86,21 +86,11 @@ /// Handles giving the revenant a new client to control it /obj/item/ectoplasm/revenant/proc/get_new_user() message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...") - var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to be [revenant.name] (reforming)?", check_jobban = ROLE_REVENANT, role = ROLE_REVENANT, poll_time = 5 SECONDS, target_mob = revenant, pic_source = revenant) - - if(!LAZYLEN(candidates)) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to be [span_notice(revenant.name)] (reforming)?", check_jobban = ROLE_REVENANT, role = ROLE_REVENANT, poll_time = 5 SECONDS, checked_target = revenant, alert_pic = revenant, role_name_text = "reforming revenant", chat_text_border_icon = revenant) + if(isnull(chosen_one)) message_admins("No candidates were found for the new revenant.") inert = TRUE visible_message(span_revenwarning("[src] settles down and seems lifeless.")) qdel(revenant) return null - - var/mob/dead/observer/potential_client = pick(candidates) - if(isnull(potential_client)) - qdel(revenant) - message_admins("No candidate was found for the new revenant. Oh well!") - inert = TRUE - visible_message(span_revenwarning("[src] settles down and seems lifeless.")) - return null - - return potential_client + return chosen_one 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 index 56aacb11a9618..8682c8028e32e 100644 --- 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 @@ -22,7 +22,7 @@ controller.clear_blackboard_key(target_key) var/turf/our_turf = get_turf(spider) - if (is_valid_web_turf(our_turf)) + if (is_valid_web_turf(our_turf, spider)) controller.set_blackboard_key(target_key, our_turf) finish_action(controller, succeeded = TRUE) return @@ -31,7 +31,7 @@ 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)) + if (!is_valid_web_turf(turf_in_view, spider)) continue turfs_by_range["[get_dist(our_turf, turf_in_view)]"] += turf_in_view diff --git a/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/web.dm b/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/web.dm index fa44cb35b2d12..5bb27b5109cbd 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/web.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/web.dm @@ -3,7 +3,7 @@ name = "Spin Web" desc = "Spin a web to slow down potential prey." button_icon = 'icons/mob/actions/actions_animal.dmi' - button_icon_state = "lay_web" + button_icon_state = "spider_web" background_icon_state = "bg_alien" overlay_icon_state = "bg_alien_border" cooldown_time = 0 SECONDS @@ -17,10 +17,12 @@ . = ..() if (!owner) return + ADD_TRAIT(owner, TRAIT_WEB_WEAVER, REF(src)) RegisterSignals(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_status_on_signal)) /datum/action/cooldown/mob_cooldown/lay_web/Remove(mob/removed_from) . = ..() + REMOVE_TRAIT(removed_from, TRAIT_WEB_WEAVER, REF(src)) UnregisterSignal(removed_from, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED)) /datum/action/cooldown/mob_cooldown/lay_web/IsAvailable(feedback = FALSE) @@ -72,7 +74,7 @@ /// Variant for genetics, created webs only allow the creator passage /datum/action/cooldown/mob_cooldown/lay_web/genetic desc = "Spin a web. Only you will be able to traverse your web easily." - cooldown_time = 4 SECONDS //the same time to lay a web + cooldown_time = 4 SECONDS /datum/action/cooldown/mob_cooldown/lay_web/genetic/plant_web(turf/target_turf, obj/structure/spider/stickyweb/existing_web) new /obj/structure/spider/stickyweb/genetic(target_turf, owner) @@ -94,20 +96,20 @@ /datum/action/cooldown/mob_cooldown/lay_web/solid_web name = "Spin Solid Web" desc = "Spin a web to obstruct potential prey." - button_icon_state = "lay_solid_web" + button_icon_state = "spider_wall" cooldown_time = 0 SECONDS webbing_time = 5 SECONDS /datum/action/cooldown/mob_cooldown/lay_web/solid_web/obstructed_by_other_web() - return !!(locate(/obj/structure/spider/solid) in get_turf(owner)) + return !!(locate(/obj/structure/spider/stickyweb/sealed/tough) in get_turf(owner)) /datum/action/cooldown/mob_cooldown/lay_web/solid_web/plant_web(turf/target_turf, obj/structure/spider/stickyweb/existing_web) - new /obj/structure/spider/solid(target_turf) + new /obj/structure/spider/stickyweb/sealed/tough(target_turf) /datum/action/cooldown/mob_cooldown/lay_web/web_passage name = "Spin Web Passage" desc = "Spin a web passage to hide the nest from prey view." - button_icon_state = "lay_web_passage" + button_icon_state = "spider_roof" cooldown_time = 0 SECONDS webbing_time = 4 SECONDS @@ -120,20 +122,20 @@ /datum/action/cooldown/mob_cooldown/lay_web/sticky_web name = "Spin Sticky Web" desc = "Spin a sticky web to trap intruders." - button_icon_state = "lay_sticky_web" + button_icon_state = "spider_ropes" cooldown_time = 20 SECONDS webbing_time = 3 SECONDS /datum/action/cooldown/mob_cooldown/lay_web/sticky_web/obstructed_by_other_web() - return !!(locate(/obj/structure/spider/sticky) in get_turf(owner)) + return !!(locate(/obj/structure/spider/stickyweb/very_sticky) in get_turf(owner)) /datum/action/cooldown/mob_cooldown/lay_web/sticky_web/plant_web(turf/target_turf, obj/structure/spider/stickyweb/existing_web) - new /obj/structure/spider/sticky(target_turf) + new /obj/structure/spider/stickyweb/very_sticky(target_turf) /datum/action/cooldown/mob_cooldown/lay_web/web_spikes name = "Spin Web Spikes" desc = "Extrude silk spikes to dissuade invaders." - button_icon_state = "lay_web_spikes" + button_icon_state = "spider_spikes" cooldown_time = 40 SECONDS webbing_time = 3 SECONDS @@ -174,12 +176,12 @@ /datum/action/cooldown/mob_cooldown/lay_web/web_reflector name = "Spin reflective silk screen" desc = "Spin a web to reflect missiles from the nest." - button_icon_state = "lay_web_reflector" + button_icon_state = "spider_mirror" cooldown_time = 30 SECONDS webbing_time = 4 SECONDS /datum/action/cooldown/mob_cooldown/lay_web/web_reflector/obstructed_by_other_web() - return !!(locate(/obj/structure/spider/reflector) in get_turf(owner)) + return !!(locate(/obj/structure/spider/stickyweb/sealed/reflector) in get_turf(owner)) /datum/action/cooldown/mob_cooldown/lay_web/web_reflector/plant_web(turf/target_turf, obj/structure/spider/stickyweb/existing_web) - new /obj/structure/spider/reflector(target_turf) + new /obj/structure/spider/stickyweb/sealed/reflector(target_turf) 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 814500674fcc8..3ddbc2364e81c 100644 --- a/code/modules/mob/living/basic/space_fauna/statue/statue.dm +++ b/code/modules/mob/living/basic/space_fauna/statue/statue.dm @@ -54,7 +54,7 @@ /mob/living/basic/statue/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_UNOBSERVANT, INNATE_TRAIT) + add_traits(list(TRAIT_MUTE, TRAIT_UNOBSERVANT), INNATE_TRAIT) AddComponent(/datum/component/unobserved_actor, unobserved_flags = NO_OBSERVED_MOVEMENT | NO_OBSERVED_ATTACKS) var/static/list/innate_actions = list( @@ -69,14 +69,8 @@ /mob/living/basic/statue/med_hud_set_status() return //we're a statue we're invincible -/mob/living/basic/statue/can_speak(allow_mimes = FALSE) - return FALSE // We're a statue, of course we can't talk. - // Cannot talk -/mob/living/basic/statue/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - return - // Turn to dust when gibbed /mob/living/basic/statue/gib() diff --git a/code/modules/mob/living/brain/brain_say.dm b/code/modules/mob/living/brain/brain_say.dm index 79e8e1d307933..df3601c1bef94 100644 --- a/code/modules/mob/living/brain/brain_say.dm +++ b/code/modules/mob/living/brain/brain_say.dm @@ -1,14 +1,25 @@ -/mob/living/brain/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterpoof = null, message_range = 7, datum/saymode/saymode = null) - if(!(container && istype(container, /obj/item/mmi))) - return //No MMI, can't speak, bucko./N - else - if(prob(emp_damage*4)) - if(prob(10))//10% chane to drop the message entirely - return - else - message = Gibberish(message, emp_damage >= 12)//scrambles the message, gets worse when emp_damage is higher +/mob/living/brain/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) + if(prob(emp_damage * 4)) + if(prob(10)) //10% chance to drop the message entirely + return + message = Gibberish(message, emp_damage >= 12)//scrambles the message, gets worse when emp_damage is higher + + return ..() - ..() +/mob/living/brain/can_speak(allow_mimes) + return istype(container, /obj/item/mmi) && ..() /mob/living/brain/radio(message, list/message_mods = list(), list/spans, language) if(message_mods[MODE_HEADSET] && istype(container, /obj/item/mmi)) diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index eeb33bd3e891f..c7c4b1ed06f3e 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -90,15 +90,18 @@ return bursting = TRUE - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), gib_on_success) - owner.AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_ALIEN_LARVA, \ - job_bans = ROLE_ALIEN, \ - to_call = to_call, \ - custom_message = "An alien is bursting out of [owner.real_name]", \ - title = "alien larva" \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + question = "An [span_notice("alien")] is bursting out of [span_danger(owner.real_name)]!", + role = ROLE_ALIEN, + check_jobban = ROLE_ALIEN, + poll_time = 20 SECONDS, + checked_target = src, + ignore_category = POLL_IGNORE_ALIEN_LARVA, + alert_pic = owner, + role_name_text = "alien larva", + chat_text_border_icon = /mob/living/carbon/alien/larva, ) + on_poll_concluded(gib_on_success, chosen_one) /// Poll has concluded with a suitor /obj/item/organ/internal/body_egg/alien_embryo/proc/on_poll_concluded(gib_on_success, mob/dead/observer/ghost) diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index d900706f102ac..cb35aebfb0770 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -8,14 +8,14 @@ /mob/living/carbon/Move(NewLoc, direct) . = ..() - if(. && !(movement_type & FLOATING)) //floating is easy - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - set_nutrition(NUTRITION_LEVEL_FED - 1) //just less than feeling vigorous - else if(nutrition && stat != DEAD) - adjust_nutrition(-(HUNGER_FACTOR/10)) - if(move_intent == MOVE_INTENT_RUN) - adjust_nutrition(-(HUNGER_FACTOR/10)) - + if(!. || (movement_type & FLOATING)) //floating is easy + return + if(nutrition <= 0 || stat == DEAD) + return + var/hunger_loss = HUNGER_FACTOR / 10 + if(move_intent == MOVE_INTENT_RUN) + hunger_loss *= 2 + adjust_nutrition(-1 * hunger_loss) /mob/living/carbon/set_usable_legs(new_value) . = ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 29b4e1f0793d3..b20cb463c833b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -41,10 +41,11 @@ /mob/living/carbon/human/proc/setup_mood() if (CONFIG_GET(flag/disable_human_mood)) return - if (isdummy(src)) - return mob_mood = new /datum/mood(src) +/mob/living/carbon/human/dummy/setup_mood() + return + /// This proc is for holding effects applied when a mob is missing certain organs /// It is called very, very early in human init because all humans innately spawn with no organs and gain them during init /// Gaining said organs removes these effects @@ -987,16 +988,6 @@ remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying) -/mob/living/carbon/human/adjust_nutrition(change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - return FALSE - return ..() - -/mob/living/carbon/human/set_nutrition(change) //Seriously fuck you oldcoders. - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - return FALSE - return ..() - /mob/living/carbon/human/is_bleeding() if(HAS_TRAIT(src, TRAIT_NOBLOOD)) return FALSE diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm index c1ddf8f62410f..8bf21e3c809a6 100644 --- a/code/modules/mob/living/carbon/human/human_say.dm +++ b/code/modules/mob/living/carbon/human/human_say.dm @@ -1,6 +1,18 @@ -/mob/living/carbon/human/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced, filterproof, message_range, datum/saymode/saymode) +/mob/living/carbon/human/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(!HAS_TRAIT(src, TRAIT_SPEAKS_CLEARLY)) var/static/regex/tongueless_lower = new("\[gdntke]+", "g") var/static/regex/tongueless_upper = new("\[GDNTKE]+", "g") diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm index 9a4a55bb7ac1f..89cf7e01ce6ff 100644 --- a/code/modules/mob/living/carbon/human/init_signals.dm +++ b/code/modules/mob/living/carbon/human/init_signals.dm @@ -5,6 +5,9 @@ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_DWARF), SIGNAL_REMOVETRAIT(TRAIT_DWARF)), PROC_REF(on_dwarf_trait)) RegisterSignal(src, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, PROC_REF(get_name_part)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT)), PROC_REF(on_fat)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_NOHUNGER), SIGNAL_REMOVETRAIT(TRAIT_NOHUNGER)), PROC_REF(on_nohunger)) + /// Gaining or losing [TRAIT_UNKNOWN] updates our name and our sechud /mob/living/carbon/human/proc/on_unknown_trait(datum/source) SIGNAL_HANDLER @@ -38,3 +41,25 @@ if(name != voice_name) voice_name += " (as [get_id_name("Unknown")])" stored_name[NAME_PART_INDEX] = voice_name + +/mob/living/carbon/human/proc/on_fat(datum/source) + SIGNAL_HANDLER + hud_used?.hunger?.update_appearance() + mob_mood?.update_nutrition_moodlets() + + if(HAS_TRAIT(src, TRAIT_FAT)) + add_movespeed_modifier(/datum/movespeed_modifier/obesity) + else + remove_movespeed_modifier(/datum/movespeed_modifier/obesity) + +/mob/living/carbon/human/proc/on_nohunger(datum/source) + SIGNAL_HANDLER + // When gaining NOHUNGER, we restore nutrition to normal levels, since we no longer interact with the hunger system + if(HAS_TRAIT(src, TRAIT_NOHUNGER)) + set_nutrition(NUTRITION_LEVEL_FED, forced = TRUE) + satiety = 0 + overeatduration = 0 + REMOVE_TRAIT(src, TRAIT_FAT, OBESITY) + else + hud_used?.hunger?.update_appearance() + mob_mood?.update_nutrition_moodlets() diff --git a/code/modules/mob/living/carbon/human/species_types/abductors.dm b/code/modules/mob/living/carbon/human/species_types/abductors.dm index 1eae13b0a5b28..f3189491f5ae9 100644 --- a/code/modules/mob/living/carbon/human/species_types/abductors.dm +++ b/code/modules/mob/living/carbon/human/species_types/abductors.dm @@ -39,8 +39,6 @@ var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR] abductor_hud.show_to(C) - C.set_safe_hunger_level() - /datum/species/abductor/on_species_loss(mob/living/carbon/C) . = ..() var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR] diff --git a/code/modules/mob/living/carbon/human/species_types/android.dm b/code/modules/mob/living/carbon/human/species_types/android.dm index 2c006d2e936b6..b7a6c532106ff 100644 --- a/code/modules/mob/living/carbon/human/species_types/android.dm +++ b/code/modules/mob/living/carbon/human/species_types/android.dm @@ -47,11 +47,6 @@ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/robot/android, ) -/datum/species/android/on_species_gain(mob/living/carbon/C) - . = ..() - // Androids don't eat, hunger or metabolise foods. Let's do some cleanup. - C.set_safe_hunger_level() - /datum/species/android/get_physical_attributes() return "Androids are almost, but not quite, identical to fully augmented humans. \ Unlike those, though, they're completely immune to toxin damage, don't have blood or organs (besides their head), don't get hungry, and can reattach their limbs! \ 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 1407ba80f7553..1d7c328f88232 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -58,7 +58,6 @@ eyes.bodypart_insert(my_head) human.update_body() head.update_icon_dropped() - human.set_safe_hunger_level() RegisterSignal(head, COMSIG_QDELETING, PROC_REF(on_head_destroyed)) /// If we gained a new body part, it had better not be a head 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 51f7426c6dfe9..3d45b2e735010 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -62,7 +62,6 @@ RegisterSignal(new_ethereal, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(refresh_light_color)) ethereal_light = new_ethereal.mob_light(light_type = /obj/effect/dummy/lighting_obj/moblight/species) refresh_light_color(new_ethereal) - new_ethereal.set_safe_hunger_level() update_mail_goodies(new_ethereal) var/obj/item/organ/internal/heart/ethereal/ethereal_heart = new_ethereal.get_organ_slot(ORGAN_SLOT_HEART) diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index ad6e36b527108..9bea2850617ba 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -60,10 +60,6 @@ /// If the bones themselves are burning clothes won't help you much var/internal_fire = FALSE -/datum/species/plasmaman/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) - . = ..() - C.set_safe_hunger_level() - /datum/species/plasmaman/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired) . = ..() var/atmos_sealed = TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index a20e240529fb2..4718645b56346 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -44,10 +44,6 @@ BODY_ZONE_CHEST = /obj/item/bodypart/chest/skeleton, ) -/datum/species/skeleton/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) - . = ..() - C.set_safe_hunger_level() - /datum/species/skeleton/check_roundstart_eligible() if(check_holidays(HALLOWEEN)) return TRUE 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 427f1f5f71bf9..2b14969f548cb 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -39,7 +39,6 @@ to_chat(new_vampire, "[info_text]") new_vampire.skin_tone = "albino" new_vampire.update_body(0) - new_vampire.set_safe_hunger_level() RegisterSignal(new_vampire, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(damage_weakness)) /datum/species/vampire/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 86f93a344c6f7..3738331e57b0a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -497,7 +497,7 @@ log_message("points at [pointing_at]", LOG_EMOTE) visible_message("[span_name("[src]")] points at [pointing_at].", span_notice("You point at [pointing_at].")) -/mob/living/verb/succumb(whispered as null) +/mob/living/verb/succumb(whispered as num|null) set hidden = TRUE if (!CAN_SUCCUMB(src)) if(HAS_TRAIT(src, TRAIT_SUCCUMB_OVERRIDE)) @@ -929,6 +929,8 @@ // I don't really care to keep this under a flag set_nutrition(NUTRITION_LEVEL_FED + 50) + overeatduration = 0 + satiety = 0 // These should be tracked by status effects losebreath = 0 @@ -2327,27 +2329,6 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/carbon/human/will_escape_storage() return TRUE -/// Sets the mob's hunger levels to a safe overall level. Useful for TRAIT_NOHUNGER species changes. -/mob/living/proc/set_safe_hunger_level() - // Nutrition reset and alert clearing. - nutrition = NUTRITION_LEVEL_FED - clear_alert(ALERT_NUTRITION) - satiety = 0 - - // Trait removal if obese - if(HAS_TRAIT_FROM(src, TRAIT_FAT, OBESITY)) - if(overeatduration >= (200 SECONDS)) - to_chat(src, span_notice("Your transformation restores your body's natural fitness!")) - - REMOVE_TRAIT(src, TRAIT_FAT, OBESITY) - remove_movespeed_modifier(/datum/movespeed_modifier/obesity) - update_worn_undersuit() - update_worn_oversuit() - - // Reset overeat duration. - overeatduration = 0 - - /// Changes the value of the [living/body_position] variable. Call this before set_lying_angle() /mob/living/proc/set_body_position(new_value) if(body_position == new_value) @@ -2598,10 +2579,9 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(isnull(guardian_client)) return else if(guardian_client == "Poll Ghosts") - var/list/candidates = SSpolling.poll_ghost_candidates("Do you want to play as an admin created Guardian Spirit of [real_name]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_HOLOPARASITE, pic_source = src, role_name_text = "guardian spirit") - if(LAZYLEN(candidates)) - var/mob/dead/observer/candidate = pick(candidates) - guardian_client = candidate.client + var/mob/chosen_one = SSpolling.poll_ghost_candidates("Do you want to play as an admin created [span_notice("Guardian Spirit")] of [span_danger(real_name)]?", check_jobban = ROLE_PAI, poll_time = 10 SECONDS, ignore_category = POLL_IGNORE_HOLOPARASITE, alert_pic = mutable_appearance('icons/mob/nonhuman-player/guardian.dmi', "magicexample"), jump_target = src, role_name_text = "guardian spirit", amount_to_pick = 1) + if(chosen_one) + guardian_client = chosen_one.client else tgui_alert(admin, "No ghost candidates.", "Guardian Controller") return @@ -2648,3 +2628,20 @@ GLOBAL_LIST_EMPTY(fire_appearances) end_look_down() else look_down() + +/** + * Totals the physical cash on the mob and returns the total. + */ +/mob/living/verb/tally_physical_credits() + //Here is all the possible non-ID payment methods. + var/list/counted_money = list() + var/physical_cash_total = 0 + for(var/obj/item/credit as anything in typecache_filter_list(get_all_contents(), GLOB.allowed_money)) //Coins, cash, and credits. + physical_cash_total += credit.get_item_credit_value() + counted_money += credit + + if(is_type_in_typecache(pulling, GLOB.allowed_money)) //Coins(Pulled). + var/obj/item/counted_credit = pulling + physical_cash_total += counted_credit.get_item_credit_value() + counted_money += counted_credit + return round(physical_cash_total) diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 0751cc37d64a9..1298a5195f830 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -94,13 +94,24 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( return new_msg -/mob/living/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if(sanitize) message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) if(!message || message == "") return - var/list/message_mods = list() var/original_message = message message = get_message_mods(message, message_mods) saymode = SSradio.saymodes[message_mods[RADIO_KEY]] @@ -206,7 +217,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( message = "[randomnote] [message] [randomnote]" spans |= SPAN_SINGING - if(LAZYACCESS(message_mods,WHISPER_MODE)) // whisper away + if(message_mods[WHISPER_MODE]) // whisper away spans |= SPAN_ITALICS if(!message) @@ -222,6 +233,9 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( return var/radio_return = radio(message, message_mods, spans, language)//roughly 27% of living/say()'s total cost + if(radio_return & NOPASS) + return TRUE + if(radio_return & ITALICS) spans |= SPAN_ITALICS if(radio_return & REDUCE_RANGE) @@ -229,8 +243,6 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( if(!message_mods[WHISPER_MODE]) message_mods[WHISPER_MODE] = MODE_WHISPER message_mods[SAY_MOD_VERB] = say_mod(message, message_mods) - if(radio_return & NOPASS) - return TRUE //No screams in space, unless you're next to someone. var/turf/T = get_turf(src) @@ -426,38 +438,6 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( /mob/proc/binarycheck() return FALSE -/mob/living/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE) - if(!..()) - return FALSE - var/sigreturn = SEND_SIGNAL(src, COMSIG_LIVING_TRY_SPEECH, message, ignore_spam, forced) - if(sigreturn & COMPONENT_CAN_ALWAYS_SPEAK) - return TRUE - - if(sigreturn & COMPONENT_CANNOT_SPEAK) - return FALSE - - if(!can_speak()) - if(HAS_MIND_TRAIT(src, TRAIT_MIMING)) - to_chat(src, span_green("Your vow of silence prevents you from speaking!")) - else - to_chat(src, span_warning("You find yourself unable to speak!")) - return FALSE - - return TRUE - -/mob/living/can_speak(allow_mimes = FALSE) - if(!allow_mimes && HAS_MIND_TRAIT(src, TRAIT_MIMING)) - return FALSE - - if(HAS_TRAIT(src, TRAIT_MUTE)) - return FALSE - - if(is_muzzled()) - return FALSE - - return TRUE - - /** * Treats the passed message with things that may modify speech (stuttering, slurring etc). * diff --git a/code/modules/mob/living/silicon/ai/ai_say.dm b/code/modules/mob/living/silicon/ai/ai_say.dm index 643149dc6163a..5434a44cc1572 100644 --- a/code/modules/mob/living/silicon/ai/ai_say.dm +++ b/code/modules/mob/living/silicon/ai/ai_say.dm @@ -1,5 +1,17 @@ -/mob/living/silicon/ai/say(message, bubble_type,list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - if(parent && istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. +/mob/living/silicon/ai/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) + if(istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. return parent.say(arglist(args)) return ..() diff --git a/code/modules/mob/living/silicon/silicon_say.dm b/code/modules/mob/living/silicon/silicon_say.dm index 54c4a4f82d1c6..4592097dfa8e5 100644 --- a/code/modules/mob/living/silicon/silicon_say.dm +++ b/code/modules/mob/living/silicon/silicon_say.dm @@ -17,6 +17,11 @@ spans ) + var/namepart = name + // If carbon, use voice to account for voice changers + if(iscarbon(src)) + namepart = GetVoice() + for(var/mob/M in GLOB.player_list) if(M.binarycheck()) if(isAI(M)) @@ -24,7 +29,7 @@ M, span_binarysay("\ Robotic Talk, \ - [span_name("[name] ([designation])")] \ + [span_name("[namepart] ([designation])")] \ [quoted_message]\ "), avoid_highlighting = src == M @@ -34,7 +39,7 @@ M, span_binarysay("\ Robotic Talk, \ - [span_name("[name]")] [quoted_message]\ + [span_name("[namepart]")] [quoted_message]\ "), avoid_highlighting = src == M ) @@ -56,7 +61,7 @@ span_binarysay("\ [follow_link] \ Robotic Talk, \ - [span_name("[name]")] [quoted_message]\ + [span_name("[namepart]")] [quoted_message]\ "), avoid_highlighting = src == M ) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 6386fa272b7eb..41fdf26250a19 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -56,11 +56,8 @@ M.take_damage(50, BRUTE, MELEE, 1) //Elites can't talk (normally)! -/mob/living/simple_animal/hostile/asteroid/elite/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) - if(can_talk) - . = ..() - return TRUE - return FALSE +/mob/living/simple_animal/hostile/asteroid/elite/can_speak(allow_mimes) + return can_talk && ..() /*Basic setup for elite attacks, based on Whoneedspace's megafauna attack setup. While using this makes the system rely on OnFire, it still gives options for timers not tied to OnFire, and it makes using attacks consistent accross the board for player-controlled elites.*/ @@ -187,10 +184,10 @@ While using this makes the system rely on OnFire, it still gives options for tim addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30) return visible_message(span_boldwarning("Something within [src] stirs...")) - var/list/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as a lavaland elite?", check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, poll_time = 5 SECONDS, target_mob = src, ignore_category = POLL_IGNORE_LAVALAND_ELITE, pic_source = src, role_name_text = "lavaland elite") - if(candidates.len) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, poll_time = 5 SECONDS, checked_target = src, ignore_category = POLL_IGNORE_LAVALAND_ELITE, alert_pic = src, role_name_text = "lavaland elite") + if(chosen_one) audible_message(span_boldwarning("The stirring sounds increase in volume!")) - elitemind = pick(candidates) + elitemind = chosen_one elitemind.playsound_local(get_turf(elitemind), 'sound/effects/magic.ogg', 40, 0) to_chat(elitemind, "You have been chosen to play as a Lavaland Elite.\nIn a few seconds, you will be summoned on Lavaland as a monster to fight your activator, in a fight to the death.\n\ Your attacks can be switched using the buttons on the top left of the HUD, and used by clicking on targets or tiles similar to a gun.\n\ 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 9517bce4f92e7..77557879ccb93 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 @@ -63,9 +63,11 @@ /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/become_ghost() icon_state = "herald_ghost" -/mob/living/simple_animal/hostile/asteroid/elite/herald/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/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) . = ..() - playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) + if(stat != CONSCIOUS) + return + playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) /datum/action/innate/elite_attack/herald_trishot name = "Triple Shot" diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/wolf.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/wolf.dm deleted file mode 100644 index 56a8c77e2fd8b..0000000000000 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/wolf.dm +++ /dev/null @@ -1,72 +0,0 @@ -/mob/living/simple_animal/hostile/asteroid/wolf - name = "white wolf" - desc = "A beast that survives by feasting on weaker opponents, they're much stronger with numbers." - icon = 'icons/mob/simple/icemoon/icemoon_monsters.dmi' - icon_state = "whitewolf" - icon_living = "whitewolf" - icon_dead = "whitewolf_dead" - mob_biotypes = MOB_ORGANIC|MOB_BEAST - mouse_opacity = MOUSE_OPACITY_ICON - friendly_verb_continuous = "howls at" - friendly_verb_simple = "howl at" - speak_emote = list("howls") - speed = 5 - move_to_delay = 5 - maxHealth = 130 - health = 130 - obj_damage = 15 - melee_damage_lower = 7.5 - melee_damage_upper = 7.5 - rapid_melee = 2 // every second attack - dodging = TRUE - dodge_prob = 50 - attack_verb_continuous = "bites" - attack_verb_simple = "bite" - attack_sound = 'sound/weapons/bite.ogg' - attack_vis_effect = ATTACK_EFFECT_BITE - vision_range = 7 - aggro_vision_range = 7 - move_force = MOVE_FORCE_WEAK - move_resist = MOVE_FORCE_WEAK - pull_force = MOVE_FORCE_WEAK - butcher_results = list(/obj/item/food/meat/slab = 2, /obj/item/stack/sheet/sinew/wolf = 2, /obj/item/stack/sheet/bone = 2) - loot = list() - crusher_loot = /obj/item/crusher_trophy/wolf_ear - stat_attack = HARD_CRIT - robust_searching = TRUE - footstep_type = FOOTSTEP_MOB_CLAW - /// Message for when the wolf decides to start running away - var/retreat_message_said = FALSE - -/mob/living/simple_animal/hostile/asteroid/wolf/Move(atom/newloc) - if(newloc && newloc.z == z && (islava(newloc) || ischasm(newloc))) - return FALSE - return ..() - -/mob/living/simple_animal/hostile/asteroid/wolf/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(stat == DEAD || health > maxHealth*0.1) - retreat_distance = initial(retreat_distance) - return - if(!retreat_message_said && target) - visible_message(span_danger("The [name] tries to flee from [target]!")) - retreat_message_said = TRUE - retreat_distance = 30 - -/mob/living/simple_animal/hostile/asteroid/wolf/Life(seconds_per_tick = SSMOBS_DT, times_fired) - . = ..() - if(!. || target) - return - retreat_message_said = FALSE - -/obj/item/crusher_trophy/wolf_ear - name = "wolf ear" - desc = "It's a wolf ear." - icon_state = "wolf_ear" - denied_type = /obj/item/crusher_trophy/wolf_ear - -/obj/item/crusher_trophy/wolf_ear/effect_desc() - return "mark detonation to gain a slight speed boost temporarily" - -/obj/item/crusher_trophy/wolf_ear/on_mark_detonation(mob/living/target, mob/living/user) - user.apply_status_effect(/datum/status_effect/speed_boost, 1 SECONDS) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 96beb024fe922..c0e2058f23ed2 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -303,6 +303,7 @@ Knockdown(amount) Stun(amount) Immobilize(amount) + Unconscious(amount) /mob/living/proc/SetAllImmobility(amount) @@ -310,6 +311,7 @@ SetKnockdown(amount) SetStun(amount) SetImmobilized(amount) + SetUnconscious(amount) /mob/living/proc/AdjustAllImmobility(amount) @@ -317,6 +319,7 @@ AdjustKnockdown(amount) AdjustStun(amount) AdjustImmobilized(amount) + AdjustUnconscious(amount) /* UNCONSCIOUS */ diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 0085d0b2fb693..619ead060ba11 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1502,12 +1502,28 @@ get_language_holder().open_language_menu(usr) ///Adjust the nutrition of a mob -/mob/proc/adjust_nutrition(change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks +/mob/proc/adjust_nutrition(change, forced = FALSE) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + return + nutrition = max(0, nutrition + change) + hud_used?.hunger?.update_appearance() + +/mob/living/adjust_nutrition(change, forced) + . = ..() + mob_mood?.update_nutrition_moodlets() ///Force set the mob nutrition -/mob/proc/set_nutrition(change) //Seriously fuck you oldcoders. - nutrition = max(0, change) +/mob/proc/set_nutrition(set_to, forced = FALSE) //Seriously fuck you oldcoders. + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + return + + nutrition = max(0, set_to) + hud_used?.hunger?.update_appearance() + +/mob/living/set_nutrition(set_to, forced) + . = ..() + mob_mood?.update_nutrition_moodlets() ///Apply a proper movespeed modifier based on items we have equipped /mob/proc/update_equipment_speed_mods() diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index d2b8ce0f3c380..7a8c993f26b0f 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -355,23 +355,22 @@ if(usr) log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") message_admins("[key_name_admin(usr)] has offered control of ([ADMIN_LOOKUPFLW(M)]) to ghosts") - var/poll_message = "Do you want to play as [M.real_name]?" + var/poll_message = "Do you want to play as [span_danger(M.real_name)]?" if(M.mind) - poll_message = "[poll_message] Job: [M.mind.assigned_role.title]." + poll_message = "[poll_message] Job: [span_notice(M.mind.assigned_role.title)]." if(M.mind.special_role) - poll_message = "[poll_message] Status: [M.mind.special_role]." + poll_message = "[poll_message] Status: [span_boldnotice(M.mind.special_role)]." else var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/) if(A) - poll_message = "[poll_message] Status: [A.name]." - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob(poll_message, check_jobban = ROLE_PAI, poll_time = 10 SECONDS, target_mob = M, pic_source = M, role_name_text = "ghost control") + poll_message = "[poll_message] Status: [span_boldnotice(A.name)]." + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(poll_message, check_jobban = ROLE_PAI, poll_time = 10 SECONDS, checked_target = M, alert_pic = M, role_name_text = "ghost control") - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) + if(chosen_one) to_chat(M, "Your mob has been taken over by a ghost!") - message_admins("[key_name_admin(C)] has taken control of ([ADMIN_LOOKUPFLW(M)])") + message_admins("[key_name_admin(chosen_one)] has taken control of ([ADMIN_LOOKUPFLW(M)])") M.ghostize(FALSE) - M.key = C.key + M.key = chosen_one.key M.client?.init_verbs() return TRUE else diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index da8bd502fb1e1..6b300b2938a3f 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -52,9 +52,6 @@ QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", 1, message, TRUE), SSspeech_controller) /mob/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE) - SHOULD_CALL_PARENT(TRUE) - if(!..()) - return FALSE var/list/filter_result var/list/soft_filter_result if(client && !forced && !filterproof) @@ -88,9 +85,31 @@ return FALSE if(client.handle_spam_prevention(message, MUTE_IC)) return FALSE - // Including can_speak() here would ignore COMPONENT_CAN_ALWAYS_SPEAK in /mob/living/try_speak() + + var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_TRY_SPEECH, message, ignore_spam, forced) + if(sigreturn & COMPONENT_IGNORE_CAN_SPEAK) + return TRUE + if(sigreturn & COMPONENT_CANNOT_SPEAK) + return FALSE + + if(!..()) // the can_speak check + if(HAS_MIND_TRAIT(src, TRAIT_MIMING)) + to_chat(src, span_green("Your vow of silence prevents you from speaking!")) + else + to_chat(src, span_warning("You find yourself unable to speak!")) + return FALSE + return TRUE +/mob/can_speak(allow_mimes = FALSE) + if(!allow_mimes && HAS_MIND_TRAIT(src, TRAIT_MIMING)) + return FALSE + + if(is_muzzled()) + return FALSE + + return ..() + ///Speak as a dead person (ghost etc) /mob/proc/say_dead(message) var/name = real_name diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index 6d2a8a9850dc2..56cd102a59b8c 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -2,7 +2,7 @@ //This proc is the most basic of the procs. All it does is make a new mob on the same tile and transfer over a few variables. //Returns the new mob //Note that this proc does NOT do MMI related stuff! -/mob/proc/change_mob_type(new_type = null, turf/location = null, new_name = null as text, delete_old_mob = FALSE) +/mob/proc/change_mob_type(new_type = null, turf/location = null, new_name = null as text|null, delete_old_mob = FALSE) if(isnewplayer(src)) to_chat(usr, span_danger("Cannot convert players who have not entered yet.")) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index ca82e13e803e6..28d89cf4cb4ae 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -184,11 +184,10 @@ to_chat(src, "You are job banned from cyborg! Appeal your job ban if you want to avoid this in the future!") ghostize(FALSE) - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob("Do you want to play as [src]?", check_jobban = JOB_CYBORG, poll_time = 5 SECONDS, target_mob = src, pic_source = src, role_name_text = "cyborg") - if(LAZYLEN(candidates)) - var/mob/dead/observer/chosen_candidate = pick(candidates) - message_admins("[key_name_admin(chosen_candidate)] has taken control of ([key_name_admin(src)]) to replace a jobbanned player.") - key = chosen_candidate.key + var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_notice(src)]?", check_jobban = JOB_CYBORG, poll_time = 5 SECONDS, checked_target = src, alert_pic = src, role_name_text = "cyborg") + if(chosen_one) + message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(src)]) to replace a jobbanned player.") + key = chosen_one.key //human -> alien /mob/living/carbon/human/proc/Alienize() diff --git a/code/modules/mob_spawn/ghost_roles/spider_roles.dm b/code/modules/mob_spawn/ghost_roles/spider_roles.dm index 8ab32d9d4f405..102a73d6f92f4 100644 --- a/code/modules/mob_spawn/ghost_roles/spider_roles.dm +++ b/code/modules/mob_spawn/ghost_roles/spider_roles.dm @@ -1,5 +1,6 @@ /obj/structure/spider/eggcluster name = "egg cluster" + icon = 'icons/effects/effects.dmi' desc = "There's something alive in there, and sooner or later it's going to find its way out." icon_state = "eggs" /// Mob spawner handling the actual spawn of the spider diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 9962bc8b9a44d..f546f12dfe58c 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -788,19 +788,7 @@ /obj/item/cigbutt, ) /// Materials that will be extracted. - var/list/accepted_mats = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/silver, - /datum/material/plasma, - /datum/material/gold, - /datum/material/diamond, - /datum/material/plastic, - /datum/material/uranium, - /datum/material/bananium, - /datum/material/titanium, - /datum/material/bluespace, - ) + var/list/accepted_mats var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_obj_entered), COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON = PROC_REF(on_atom_initialized_on), @@ -810,10 +798,15 @@ /obj/item/mod/module/recycler/Initialize(mapload) . = ..() + + if(!length(accepted_mats)) + accepted_mats = SSmaterials.materials_by_category[MAT_CATEGORY_SILO] + container = AddComponent( \ /datum/component/material_container, \ - accepted_mats, 50 * SHEET_MATERIAL_AMOUNT, \ - MATCONTAINER_EXAMINE|MATCONTAINER_NO_INSERT, \ + accepted_mats, \ + 50 * SHEET_MATERIAL_AMOUNT, \ + MATCONTAINER_EXAMINE | MATCONTAINER_NO_INSERT, \ container_signals = list( \ COMSIG_MATCONTAINER_SHEETS_RETRIEVED = TYPE_PROC_REF(/obj/item/mod/module/recycler, InsertSheets) \ ) \ diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 5f718ef04f03c..53124ca5ec3ec 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -267,30 +267,34 @@ /** * InsertID - * Attempt to insert the ID in either card slot. + * Attempt to insert the ID in either card slot, if ID is present - attempts swap * Args: * inserting_id - the ID being inserted * user - The person inserting the ID */ /obj/item/modular_computer/InsertID(obj/item/card/inserting_id, mob/user) - //all slots taken - if(computer_id_slot) + if(!isnull(user) && !user.transferItemToLoc(inserting_id, src)) return FALSE - if(user) - if(!user.transferItemToLoc(inserting_id, src)) - return FALSE - to_chat(user, span_notice("You insert \the [inserting_id] into the card slot.")) else inserting_id.forceMove(src) + if(!isnull(computer_id_slot)) + RemoveID(user, silent = TRUE) + computer_id_slot = inserting_id + if(!isnull(user)) + to_chat(user, span_notice("You insert \the [inserting_id] into the card slot.")) + balloon_alert(user, "inserted ID") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + if(ishuman(loc)) var/mob/living/carbon/human/human_wearer = loc if(human_wearer.wear_id == src) human_wearer.sec_hud_set_ID() + update_appearance() update_slot_icon() SEND_SIGNAL(src, COMSIG_MODULAR_COMPUTER_INSERTED_ID, inserting_id, user) @@ -300,8 +304,9 @@ * Removes the ID card from the computer, and puts it in loc's hand if it's a mob * Args: * user - The mob trying to remove the ID, if there is one + * silent - Boolean, determines whether fluff text would be printed */ -/obj/item/modular_computer/RemoveID(mob/user) +/obj/item/modular_computer/RemoveID(mob/user, silent = FALSE) if(!computer_id_slot) return ..() @@ -314,14 +319,17 @@ computer_id_slot.forceMove(drop_location()) computer_id_slot = null - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) - balloon_alert(user, "removed ID") - to_chat(user, span_notice("You remove the card from the card slot.")) + + if(!silent && !isnull(user)) + to_chat(user, span_notice("You remove the card from the card slot.")) + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + balloon_alert(user, "removed ID") if(ishuman(loc)) var/mob/living/carbon/human/human_wearer = loc if(human_wearer.wear_id == src) human_wearer.sec_hud_set_ID() + update_slot_icon() update_appearance() return TRUE @@ -403,6 +411,10 @@ /obj/item/modular_computer/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) . = ..() + if(computer_id_slot && isidcard(held_item)) + context[SCREENTIP_CONTEXT_LMB] = "Swap ID" + . = CONTEXTUAL_SCREENTIP_SET + if(held_item?.tool_behaviour == TOOL_SCREWDRIVER && internal_cell) context[SCREENTIP_CONTEXT_RMB] = "Remove Cell" . = CONTEXTUAL_SCREENTIP_SET diff --git a/code/modules/modular_computers/computers/item/disks/role_disks.dm b/code/modules/modular_computers/computers/item/disks/role_disks.dm index da52ee76281a1..f7f20efb70b43 100644 --- a/code/modules/modular_computers/computers/item/disks/role_disks.dm +++ b/code/modules/modular_computers/computers/item/disks/role_disks.dm @@ -98,6 +98,7 @@ starting_programs = list( /datum/computer_file/program/shipping, /datum/computer_file/program/budgetorders, + /datum/computer_file/program/restock_tracker, ) /** @@ -123,6 +124,6 @@ /datum/computer_file/program/alarm_monitor, /datum/computer_file/program/atmosscan, /datum/computer_file/program/supermatter_monitor, - + ) diff --git a/code/modules/modular_computers/computers/item/role_tablet_presets.dm b/code/modules/modular_computers/computers/item/role_tablet_presets.dm index 205c6a0c422e5..a701d9fd4108d 100644 --- a/code/modules/modular_computers/computers/item/role_tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/role_tablet_presets.dm @@ -114,6 +114,7 @@ /datum/computer_file/program/robocontrol, /datum/computer_file/program/budgetorders, /datum/computer_file/program/shipping, + /datum/computer_file/program/restock_tracker, ) /** @@ -264,6 +265,7 @@ /datum/computer_file/program/shipping, /datum/computer_file/program/budgetorders, /datum/computer_file/program/robocontrol, + /datum/computer_file/program/restock_tracker, ) /obj/item/modular_computer/pda/shaftminer diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index 238d05704e23d..a9bbff8db1b91 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -51,7 +51,7 @@ authenticated_user = auth_card.registered_name ? auth_card.registered_name : "Unknown" job_templates = is_centcom ? SSid_access.centcom_job_templates.Copy() : SSid_access.station_job_templates.Copy() valid_access = is_centcom ? SSid_access.get_region_access_list(list(REGION_CENTCOM)) : SSid_access.get_region_access_list(list(REGION_ALL_STATION)) - update_static_data(user) + computer.update_static_data_for_all_viewers() return TRUE // Otherwise, we're minor and now we have to build a list of restricted departments we can change access for. @@ -67,7 +67,7 @@ minor = TRUE valid_access |= SSid_access.get_region_access_list(region_access) authenticated_card = "[auth_card.name] \[LIMITED ACCESS\]" - update_static_data(user) + computer.update_static_data_for_all_viewers() return TRUE return FALSE diff --git a/code/modules/modular_computers/file_system/programs/restock_tracker.dm b/code/modules/modular_computers/file_system/programs/restock_tracker.dm new file mode 100644 index 0000000000000..46462c0c6b531 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/restock_tracker.dm @@ -0,0 +1,31 @@ +/datum/computer_file/program/restock_tracker + filename = "restockapp" + filedesc = "NT Restock Tracker" + downloader_category = PROGRAM_CATEGORY_SUPPLY + program_open_overlay = "restock" + extended_desc = "Nanotrasen IoT network listing all the vending machines found on station, and how well stocked they are each. Profitable!" + program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET + can_run_on_flags = PROGRAM_LAPTOP | PROGRAM_PDA + size = 4 + program_icon = "cash-register" + tgui_id = "NtosRestock" + +/datum/computer_file/program/restock_tracker/ui_data() + var/list/data = list() + var/list/vending_list = list() + var/id_increment = 1 + for(var/obj/machinery/vending/vendor as anything in GLOB.vending_machines_to_restock) + var/stock = vendor.total_loaded_stock() + var/max_stock = vendor.total_max_stock() + if((max_stock == 0 || (stock >= max_stock)) && vendor.credits_contained == 0) + continue + vending_list += list(list( + "name" = vendor.name, + "location" = get_area_name(vendor), + "credits" = vendor.credits_contained, + "percentage" = (stock / max_stock) * 100, + "id" = id_increment, + )) + id_increment++ + data["vending_list"] = vending_list + return data diff --git a/code/modules/power/apc/apc_attack.dm b/code/modules/power/apc/apc_attack.dm index 509eb4f05b90d..fc6b33f275785 100644 --- a/code/modules/power/apc/apc_attack.dm +++ b/code/modules/power/apc/apc_attack.dm @@ -221,7 +221,7 @@ return stomach.drain_time = world.time + APC_DRAIN_TIME addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, balloon_alert), ethereal, "draining power"), alert_timer_duration) - if(do_after(user, APC_DRAIN_TIME, target = src)) + while(do_after(user, APC_DRAIN_TIME, target = src)) if(cell.charge <= (cell.maxcharge / 2) || (stomach.crystal_charge > charge_limit)) return balloon_alert(ethereal, "received charge") @@ -243,9 +243,10 @@ balloon_alert(ethereal, "can't transfer power!") return if(istype(stomach)) - balloon_alert(ethereal, "transferred power") - stomach.adjust_charge(-APC_POWER_GAIN) - cell.give(APC_POWER_GAIN) + while(do_after(user, APC_DRAIN_TIME, target = src)) + balloon_alert(ethereal, "transferred power") + stomach.adjust_charge(-APC_POWER_GAIN) + cell.give(APC_POWER_GAIN) else balloon_alert(ethereal, "can't transfer power!") diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index ec6c23b00c6f6..795ff6f099a74 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -246,7 +246,7 @@ return to_chat(H, span_notice("You begin clumsily channeling power from [src] into your body.")) stomach.drain_time = world.time + CELL_DRAIN_TIME - if(do_after(user, CELL_DRAIN_TIME, target = src)) + while(do_after(user, CELL_DRAIN_TIME, target = src)) if((charge < CELL_POWER_DRAIN) || (stomach.crystal_charge > charge_limit)) return if(istype(stomach)) diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index 45a8c71d7652a..41485539afa5b 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -122,6 +122,7 @@ /obj/machinery/light/LateInitialize() . = ..() +#ifndef MAP_TEST switch(fitting) if("tube") if(prob(2)) @@ -129,6 +130,7 @@ if("bulb") if(prob(5)) break_light_tube(TRUE) +#endif update(trigger = FALSE) /obj/machinery/light/Destroy() diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index b21b26dcea0ae..5b712d52da2ff 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -50,7 +50,7 @@ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF | PASSMACHINE | PASSSTRUCTURE | PASSDOORS flags_1 = SUPERMATTER_IGNORES_1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF | SHUTTLE_CRUSH_PROOF obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION /obj/singularity/Initialize(mapload, starting_energy = 50) diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 2b625b36d5bf7..89dec17a26eca 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -26,7 +26,7 @@ obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION pixel_x = -32 pixel_y = -32 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF | SHUTTLE_CRUSH_PROOF flags_1 = SUPERMATTER_IGNORES_1 var/energy diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 841629f5e38cf..6984d0f1bb8b1 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -130,6 +130,12 @@ ///What is the cap on our misfire probability? Do not set this to 100. var/misfire_probability_cap = 25 + /// Fire Selector Variables /// + /// Tracks the firemode of burst weapons. TRUE means it is in burst mode. + var/burst_fire_selection = FALSE + /// If it has an icon for a selector switch indicating current firemode. + var/selector_switch_icon = FALSE + /obj/item/gun/ballistic/Initialize(mapload) . = ..() if(!spawn_magazine_type) @@ -200,6 +206,14 @@ /obj/item/gun/ballistic/update_overlays() . = ..() + + if(selector_switch_icon) + switch(burst_fire_selection) + if(FALSE) + . += "[initial(icon_state)]_semi" + if(TRUE) + . += "[initial(icon_state)]_burst" + if(show_bolt_icon) if (bolt_type == BOLT_TYPE_LOCKING) . += "[icon_state]_bolt[bolt_locked ? "_locked" : ""]" @@ -249,6 +263,27 @@ if(capacity_number) . += "[icon_state]_mag_[capacity_number]" +/obj/item/gun/ballistic/ui_action_click(mob/user, actiontype) + if(istype(actiontype, /datum/action/item_action/toggle_firemode)) + burst_select() + else + ..() + +/obj/item/gun/ballistic/proc/burst_select() + var/mob/living/carbon/human/user = usr + burst_fire_selection = !burst_fire_selection + if(!burst_fire_selection) + burst_size = 1 + fire_delay = 0 + balloon_alert(user, "switched to semi-automatic") + else + burst_size = initial(burst_size) + fire_delay = initial(fire_delay) + balloon_alert(user, "switched to [burst_size]-round burst") + + playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) + update_appearance() + update_item_action_buttons() /obj/item/gun/ballistic/handle_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE) if(!semi_auto && from_firing) diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 4162ca9890f2f..3d6940692d890 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -9,41 +9,7 @@ fire_sound_volume = 90 rack_sound = 'sound/weapons/gun/smg/smgrack.ogg' suppressed_sound = 'sound/weapons/gun/smg/shot_suppressed.ogg' - var/select = 1 ///fire selector position. 1 = semi, 2 = burst. anything past that can vary between guns. - var/selector_switch_icon = FALSE ///if it has an icon for a selector switch indicating current firemode. - -/obj/item/gun/ballistic/automatic/update_overlays() - . = ..() - if(!selector_switch_icon) - return - - switch(select) - if(0) - . += "[initial(icon_state)]_semi" - if(1) - . += "[initial(icon_state)]_burst" - -/obj/item/gun/ballistic/automatic/ui_action_click(mob/user, actiontype) - if(istype(actiontype, /datum/action/item_action/toggle_firemode)) - burst_select() - else - ..() - -/obj/item/gun/ballistic/automatic/proc/burst_select() - var/mob/living/carbon/human/user = usr - select = !select - if(!select) - burst_size = 1 - fire_delay = 0 - balloon_alert(user, "switched to semi-automatic") - else - burst_size = initial(burst_size) - fire_delay = initial(fire_delay) - balloon_alert(user, "switched to [burst_size]-round burst") - - playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) - update_appearance() - update_item_action_buttons() + burst_fire_selection = TRUE /obj/item/gun/ballistic/automatic/proto name = "\improper Nanotrasen Saber SMG" @@ -205,14 +171,6 @@ else ..() -/obj/item/gun/ballistic/automatic/m90/update_overlays() - . = ..() - switch(select) - if(0) - . += "[initial(icon_state)]_semi" - if(1) - . += "[initial(icon_state)]_burst" - /obj/item/gun/ballistic/automatic/tommygun name = "\improper Thompson SMG" desc = "Based on the classic 'Chicago Typewriter'." diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 4e81b1e585638..a45511193b767 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -140,7 +140,7 @@ /obj/item/gun/ballistic/shotgun/bulldog name = "\improper Bulldog Shotgun" - desc = "A semi-auto, mag-fed shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines. Can have a secondary magazine attached to quickly swap between ammo types, or just to keep shooting." + desc = "A 2-round burst fire, mag-fed shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines. Can have a secondary magazine attached to quickly swap between ammo types, or just to keep shooting." icon_state = "bulldog" inhand_icon_state = "bulldog" worn_icon_state = "cshotgun" @@ -152,11 +152,11 @@ weapon_weight = WEAPON_MEDIUM accepted_magazine_type = /obj/item/ammo_box/magazine/m12g can_suppress = FALSE - burst_size = 1 - fire_delay = 0 + burst_size = 2 + fire_delay = 1 pin = /obj/item/firing_pin/implant/pindicate fire_sound = 'sound/weapons/gun/shotgun/shot_alt.ogg' - actions_types = list() + actions_types = list(/datum/action/item_action/toggle_firemode) mag_display = TRUE empty_indicator = TRUE empty_alarm = TRUE @@ -165,6 +165,7 @@ semi_auto = TRUE internal_magazine = FALSE tac_reloads = TRUE + burst_fire_selection = TRUE ///the type of secondary magazine for the bulldog var/secondary_magazine_type ///the secondary magazine diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index ae91fb6c60318..e52d38b3da111 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -362,24 +362,23 @@ /obj/projectile/magic/wipe/proc/possession_test(mob/living/carbon/target) var/datum/brain_trauma/special/imaginary_friend/trapped_owner/trauma = target.gain_trauma(/datum/brain_trauma/special/imaginary_friend/trapped_owner) - var/poll_message = "Do you want to play as [target.real_name]?" + var/poll_message = "Do you want to play as [span_danger(target.real_name)]?" if(target.mind) - poll_message = "[poll_message] Job:[target.mind.assigned_role.title]." + poll_message = "[poll_message] Job:[span_notice(target.mind.assigned_role.title)]." if(target.mind && target.mind.special_role) - poll_message = "[poll_message] Status:[target.mind.special_role]." + poll_message = "[poll_message] Status:[span_boldnotice(target.mind.special_role)]." else if(target.mind) var/datum/antagonist/A = target.mind.has_antag_datum(/datum/antagonist/) if(A) - poll_message = "[poll_message] Status:[A.name]." - var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob(poll_message, check_jobban = ROLE_PAI, poll_time = 10 SECONDS, target_mob = target, pic_source = target, role_name_text = "bolt of possession") + poll_message = "[poll_message] Status:[span_boldnotice(A.name)]." + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(poll_message, check_jobban = ROLE_PAI, poll_time = 10 SECONDS, checked_target = target, alert_pic = target, role_name_text = "bolt of possession") if(target.stat == DEAD)//boo. return - if(LAZYLEN(candidates)) - var/mob/dead/observer/ghost = pick(candidates) + if(chosen_one) to_chat(target, span_boldnotice("You have been noticed by a ghost and it has possessed you!")) var/oldkey = target.key target.ghostize(FALSE) - target.key = ghost.key + target.key = chosen_one.key trauma.friend.key = oldkey trauma.friend.reset_perspective(null) trauma.friend.Show() diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index 43430d0946916..c31b7e7079c97 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -639,7 +639,7 @@ . = ..() affected_mob.adjust_drowsiness(3 SECONDS * REM * seconds_per_tick) var/need_mob_update - switch(affected_mob.mob_mood.sanity_level) + switch(affected_mob.mob_mood.sanity) if (SANITY_INSANE to SANITY_CRAZY) need_mob_update = affected_mob.adjustStaminaLoss(3 * REM * seconds_per_tick, updating_stamina = FALSE) if (SANITY_UNSTABLE to SANITY_DISTURBED) diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index d200ee2dc38f8..10d69afa16108 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -87,11 +87,8 @@ to_chat(affected_mob, span_notice("[smoke_message]")) affected_mob.add_mood_event("smoked", /datum/mood_event/smoked) affected_mob.remove_status_effect(/datum/status_effect/jitter) - affected_mob.AdjustStun(-50 * REM * seconds_per_tick) - affected_mob.AdjustKnockdown(-50 * REM * seconds_per_tick) - affected_mob.AdjustUnconscious(-50 * REM * seconds_per_tick) - affected_mob.AdjustParalyzed(-50 * REM * seconds_per_tick) - affected_mob.AdjustImmobilized(-50 * REM * seconds_per_tick) + affected_mob.AdjustAllImmobility(-50 * REM * seconds_per_tick) + return UPDATE_MOB_HEALTH /datum/reagent/drug/nicotine/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) @@ -177,11 +174,7 @@ if(SPT_PROB(2.5, seconds_per_tick)) to_chat(affected_mob, span_notice("[high_message]")) affected_mob.add_mood_event("tweaking", /datum/mood_event/stimulant_medium) - affected_mob.AdjustStun(-40 * REM * seconds_per_tick) - affected_mob.AdjustKnockdown(-40 * REM * seconds_per_tick) - affected_mob.AdjustUnconscious(-40 * REM * seconds_per_tick) - affected_mob.AdjustParalyzed(-40 * REM * seconds_per_tick) - affected_mob.AdjustImmobilized(-40 * REM * seconds_per_tick) + affected_mob.AdjustAllImmobility(-40 * REM * seconds_per_tick) var/need_mob_update need_mob_update = affected_mob.adjustStaminaLoss(-2 * REM * seconds_per_tick, updating_stamina = FALSE, required_biotype = affected_biotype) affected_mob.set_jitter_if_lower(4 SECONDS * REM * seconds_per_tick) @@ -459,11 +452,7 @@ /datum/reagent/drug/maint/tar/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() - affected_mob.AdjustStun(-10 * REM * seconds_per_tick) - affected_mob.AdjustKnockdown(-10 * REM * seconds_per_tick) - affected_mob.AdjustUnconscious(-10 * REM * seconds_per_tick) - affected_mob.AdjustParalyzed(-10 * REM * seconds_per_tick) - affected_mob.AdjustImmobilized(-10 * REM * seconds_per_tick) + affected_mob.AdjustAllImmobility(-10 * REM * seconds_per_tick) affected_mob.adjustOrganLoss(ORGAN_SLOT_LIVER, 1.5 * REM * seconds_per_tick, required_organ_flag = affected_organ_flags) return UPDATE_MOB_HEALTH diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index d20f2b786277d..d2bc311579b89 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -88,11 +88,8 @@ /datum/reagent/medicine/synaptizine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() affected_mob.adjust_drowsiness(-10 SECONDS * REM * seconds_per_tick) - affected_mob.AdjustStun(-20 * REM * seconds_per_tick) - affected_mob.AdjustKnockdown(-20 * REM * seconds_per_tick) - affected_mob.AdjustUnconscious(-20 * REM * seconds_per_tick) - affected_mob.AdjustImmobilized(-20 * REM * seconds_per_tick) - affected_mob.AdjustParalyzed(-20 * REM * seconds_per_tick) + affected_mob.AdjustAllImmobility(-20 * REM * seconds_per_tick) + if(holder.has_reagent(/datum/reagent/toxin/mindbreaker)) holder.remove_reagent(/datum/reagent/toxin/mindbreaker, 5 * REM * seconds_per_tick) affected_mob.adjust_hallucinations(-20 SECONDS * REM * seconds_per_tick) diff --git a/code/modules/reagents/reagent_containers/condiment.dm b/code/modules/reagents/reagent_containers/condiment.dm index bbd0a84504fac..e34511e9e0d0e 100644 --- a/code/modules/reagents/reagent_containers/condiment.dm +++ b/code/modules/reagents/reagent_containers/condiment.dm @@ -424,6 +424,7 @@ /datum/reagent/consumable/bbqsauce = list("condi_bbq", "BBQ sauce", "Hand wipes not included."), /datum/reagent/consumable/peanut_butter = list("condi_peanutbutter", "Peanut Butter", "A creamy paste made from ground peanuts."), /datum/reagent/consumable/cherryjelly = list("condi_cherryjelly", "Cherry Jelly", "A jar of super-sweet cherry jelly."), + /datum/reagent/consumable/mayonnaise = list("condi_mayo", "Mayonnaise", "Not an instrument."), ) /// Can't use initial(name) for this. This stores the name set by condimasters. var/originalname = "condiment" @@ -516,3 +517,15 @@ originalname = "sugar" volume = 5 list_reagents = list(/datum/reagent/consumable/sugar = 5) + +/obj/item/reagent_containers/condiment/pack/soysauce + name = "soy sauce pack" + originalname = "soy sauce" + volume = 5 + list_reagents = list(/datum/reagent/consumable/soysauce = 5) + +/obj/item/reagent_containers/condiment/pack/mayonnaise + name = "mayonnaise pack" + originalname = "mayonnaise" + volume = 5 + list_reagents = list(/datum/reagent/consumable/mayonnaise = 5) diff --git a/code/modules/religion/sparring/sparring_datum.dm b/code/modules/religion/sparring/sparring_datum.dm index bfaa94d65a8aa..78de2bd3a0bb0 100644 --- a/code/modules/religion/sparring/sparring_datum.dm +++ b/code/modules/religion/sparring/sparring_datum.dm @@ -301,4 +301,4 @@ return to_chat(loser, span_userdanger("You've lost ownership over your soul to [winner]!")) var/obj/item/soulstone/anybody/chaplain/sparring/shard = new(shard_turf) - shard.capture_soul(loser, winner, forced = TRUE) + INVOKE_ASYNC(shard, TYPE_PROC_REF(/obj/item/soulstone, capture_soul), loser, winner, forced = TRUE) diff --git a/code/modules/research/designs/autolathe/engineering_designs.dm b/code/modules/research/designs/autolathe/engineering_designs.dm index 6249f5c645a1f..945966035f3d1 100644 --- a/code/modules/research/designs/autolathe/engineering_designs.dm +++ b/code/modules/research/designs/autolathe/engineering_designs.dm @@ -426,3 +426,29 @@ RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_MOUNTS, ) departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + +/datum/design/tram_floor_dark + name = "Dark Tram Tile" + id = "tram_floor_dark" + build_type = PROTOLATHE + materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 0.25) + build_path = /obj/item/stack/thermoplastic + maxstack = 50 + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_MATERIALS, + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING + +/datum/design/tram_floor_light + name = "Light Tram Tile" + id = "tram_floor_light" + build_type = PROTOLATHE + materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 0.25) + build_path = /obj/item/stack/thermoplastic/light + maxstack = 50 + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_MATERIALS, + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 66fb0a8caa3bd..c790784156dd1 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -121,6 +121,8 @@ "toy_armblade", "toy_balloon", "toygun", + "tram_floor_dark", + "tram_floor_light", "trapdoor_electronics", "turbine_part_compressor", "turbine_part_rotor", diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 49f7bfa61f392..ba568c4bc74a7 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -688,7 +688,6 @@ desc = "A miraculous chemical mix that grants human like intelligence to living beings." icon = 'icons/obj/medical/chemical.dmi' icon_state = "potpink" - var/list/not_interested = list() var/being_used = FALSE var/sentience_type = SENTIENCE_ORGANIC @@ -704,16 +703,22 @@ 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 balloon_alert(user, "invalid creature!") return - + var/potion_reason = tgui_input_text(user, "For what reason?", "Intelligence Potion", multiline = TRUE, timeout = 2 MINUTES) + if(isnull(potion_reason)) + return balloon_alert(user, "offering...") being_used = TRUE - - var/datum/callback/to_call = CALLBACK(src, PROC_REF(on_poll_concluded), user, dumb_mob) - dumb_mob.AddComponent(/datum/component/orbit_poll, \ - ignore_key = POLL_IGNORE_SENTIENCE_POTION, \ - job_bans = ROLE_SENTIENCE, \ - to_call = to_call, \ + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + question = "[span_danger(user)] is offering [span_notice(dumb_mob)] an intelligence potion! Reason: [span_boldnotice(potion_reason)]", + check_jobban = ROLE_SENTIENCE, + poll_time = 20 SECONDS, + checked_target = dumb_mob, + ignore_category = POLL_IGNORE_SENTIENCE_POTION, + alert_pic = dumb_mob, + role_name_text = "intelligence potion", + chat_text_border_icon = src, ) + on_poll_concluded(user, dumb_mob, chosen_one) /// Assign the chosen ghost to the mob /obj/item/slimepotion/slime/sentience/proc/on_poll_concluded(mob/user, mob/living/dumb_mob, mob/dead/observer/ghost) diff --git a/code/modules/shuttle/battlecruiser_starfury.dm b/code/modules/shuttle/battlecruiser_starfury.dm index e95ff4243f5d4..a27cadacad2ec 100644 --- a/code/modules/shuttle/battlecruiser_starfury.dm +++ b/code/modules/shuttle/battlecruiser_starfury.dm @@ -135,7 +135,7 @@ */ /proc/summon_battlecruiser(datum/team/battlecruiser/team) - var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for battlecruiser crew?", check_jobban = ROLE_TRAITOR, pic_source = /obj/machinery/sleeper/syndie, role_name_text = "battlecruiser crew") + var/list/candidates = SSpolling.poll_ghost_candidates("Do you wish to be considered for [span_notice("battlecruiser crew")]?", check_jobban = ROLE_TRAITOR, alert_pic = /obj/machinery/sleeper/syndie, role_name_text = "battlecruiser crew") shuffle_inplace(candidates) var/datum/map_template/ship = SSmapping.map_templates["battlecruiser_starfury.dmm"] diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index c65a9ad123a59..9b267489100ee 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -21,26 +21,22 @@ All ShuttleMove procs go here return var/shuttle_dir = shuttle.dir - for(var/i in contents) - var/atom/movable/thing = i - if(ismob(thing)) - if(isliving(thing)) - var/mob/living/M = thing - if(M.buckled) - M.buckled.unbuckle_mob(M, 1) - if(M.pulledby) - M.pulledby.stop_pulling() - M.stop_pulling() - M.visible_message(span_warning("[shuttle] slams into [M]!")) - SSblackbox.record_feedback("tally", "shuttle_gib", 1, M.type) - log_shuttle("[key_name(M)] was shuttle gibbed by [shuttle].") - M.investigate_log("has been gibbed by [shuttle].", INVESTIGATE_DEATHS) - M.gib(DROP_ALL_REMAINS) - - - else //non-living mobs shouldn't be affected by shuttles, which is why this is an else - if(istype(thing, /obj/effect/abstract) || istype(thing, /obj/singularity) || istype(thing, /obj/energy_ball)) + for(var/atom/movable/thing as anything in contents) + if(thing.resistance_flags & SHUTTLE_CRUSH_PROOF) + continue + if(isliving(thing)) + var/mob/living/living_thing = thing + if(living_thing.incorporeal_move) // Don't crush incorporeal things continue + living_thing.buckled?.unbuckle_mob(living_thing, force = TRUE) + living_thing.pulledby?.stop_pulling() + living_thing.stop_pulling() + living_thing.visible_message(span_warning("[shuttle] slams into [living_thing]!")) + SSblackbox.record_feedback("tally", "shuttle_gib", 1, living_thing.type) + log_shuttle("[key_name(living_thing)] was shuttle gibbed by [shuttle].") + living_thing.investigate_log("has been gibbed by [shuttle].", INVESTIGATE_DEATHS) + living_thing.gib(DROP_ALL_REMAINS) + else if(!ismob(thing)) //non-living mobs shouldn't be affected by shuttles, which is why this is an else if(!thing.anchored) step(thing, shuttle_dir) else diff --git a/code/modules/shuttle/shuttle_events/player_controlled.dm b/code/modules/shuttle/shuttle_events/player_controlled.dm index 77fee390a6876..86d134f29f727 100644 --- a/code/modules/shuttle/shuttle_events/player_controlled.dm +++ b/code/modules/shuttle/shuttle_events/player_controlled.dm @@ -17,17 +17,12 @@ /// Attempt to grant control of a mob to ghosts before spawning it in. if spawn_anyway_if_no_player = TRUE, we spawn the mob even if there's no ghosts /datum/shuttle_event/simple_spawner/player_controlled/proc/try_grant_ghost_control(spawn_type) - var/list/candidates = SSpolling.poll_ghost_candidates(ghost_alert_string + " (Warning: you will not be able to return to your body!)", check_jobban = role_type, poll_time = 10 SECONDS, pic_source = spawn_type, role_name_text = "shot at shuttle") - - if(!candidates.len && !spawn_anyway_if_no_player) + var/mob/chosen_one = SSpolling.poll_ghost_candidates(ghost_alert_string + " (Warning: you will not be able to return to your body!)", check_jobban = role_type, poll_time = 10 SECONDS, alert_pic = spawn_type, role_name_text = "shot at shuttle", amount_to_pick = 1) + if(isnull(chosen_one) && !spawn_anyway_if_no_player) return - var/mob/living/new_mob = new spawn_type (get_turf(get_spawn_turf())) - - if(candidates.len) - var/mob/dead/observer/candidate = pick(candidates) - new_mob.ckey = candidate.ckey - post_spawn(new_mob) + new_mob.ckey = chosen_one.ckey + post_spawn(new_mob) ///BACK FOR REVENGE!!! /datum/shuttle_event/simple_spawner/player_controlled/alien_queen diff --git a/code/modules/spells/spell_types/shapeshift/_shape_status.dm b/code/modules/spells/spell_types/shapeshift/_shape_status.dm index faa84835255a8..cffd9804ea588 100644 --- a/code/modules/spells/spell_types/shapeshift/_shape_status.dm +++ b/code/modules/spells/spell_types/shapeshift/_shape_status.dm @@ -189,6 +189,12 @@ // is no longer in control of the shapeshifted mob, such as mindswapping out of a shapeshift if(!QDELETED(source_spell) && source_spell.owner == owner) source_spell.Grant(caster_mob) + if(owner?.contents) + // Prevent round removal and consuming stuff when losing shapeshift + for(var/atom/movable/thing as anything in owner.contents) + if(thing == caster_mob) + continue + thing.forceMove(get_turf(owner)) for(var/datum/action/bodybound_action as anything in owner.actions) if(bodybound_action.target != caster_mob) diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index f1ea0c06645b4..0463349ca00ab 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -194,13 +194,9 @@ return -/obj/item/bodypart/head/talk_into(mob/holder, message, channel, spans, datum/language/language, list/message_mods) - var/mob/headholder = holder - if(istype(headholder)) - headholder.log_talk(message, LOG_SAY, tag = "beheaded talk") - - say(message, language, sanitize = FALSE) - return NOPASS +/obj/item/bodypart/head/Initialize(mapload) + . = ..() + AddElement(/datum/element/toy_talk) /obj/item/bodypart/head/GetVoice() return "The head of [real_name]" diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index a2cf91c72f5e9..6e3d9a0ee7ccb 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -157,15 +157,27 @@ /obj/item/autosurgeon/syndicate/thermal_eyes starting_organ = /obj/item/organ/internal/eyes/robotic/thermals +/obj/item/autosurgeon/syndicate/thermal_eyes/single_use + uses = 1 + /obj/item/autosurgeon/syndicate/xray_eyes starting_organ = /obj/item/organ/internal/eyes/robotic/xray +/obj/item/autosurgeon/syndicate/xray_eyes/single_use + uses = 1 + /obj/item/autosurgeon/syndicate/anti_stun starting_organ = /obj/item/organ/internal/cyberimp/brain/anti_stun +/obj/item/autosurgeon/syndicate/anti_stun/single_use + uses = 1 + /obj/item/autosurgeon/syndicate/reviver starting_organ = /obj/item/organ/internal/cyberimp/chest/reviver +/obj/item/autosurgeon/syndicate/reviver/single_use + uses = 1 + /obj/item/autosurgeon/syndicate/commsagent desc = "A device that automatically - painfully - inserts an implant. It seems someone's specially \ modified this one to only insert... tongues. Horrifying." @@ -178,6 +190,9 @@ /obj/item/autosurgeon/syndicate/emaggedsurgerytoolset starting_organ = /obj/item/organ/internal/cyberimp/arm/surgery/emagged +/obj/item/autosurgeon/syndicate/emaggedsurgerytoolset/single_use + uses = 1 + /obj/item/autosurgeon/syndicate/contraband_sechud desc = "Contains a contraband SecHUD implant, undetectable by health scanners." uses = 1 diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm index 6e985e26329b6..b055503c028b3 100644 --- a/code/modules/surgery/organs/internal/stomach/_stomach.dm +++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm @@ -128,16 +128,11 @@ if(human.overeatduration < (200 SECONDS)) to_chat(human, span_notice("You feel fit again!")) REMOVE_TRAIT(human, TRAIT_FAT, OBESITY) - human.remove_movespeed_modifier(/datum/movespeed_modifier/obesity) - human.update_worn_undersuit() - human.update_worn_oversuit() + else if(human.overeatduration >= (200 SECONDS)) to_chat(human, span_danger("You suddenly feel blubbery!")) ADD_TRAIT(human, TRAIT_FAT, OBESITY) - human.add_movespeed_modifier(/datum/movespeed_modifier/obesity) - human.update_worn_undersuit() - human.update_worn_oversuit() // nutrition decrease and satiety if (human.nutrition > 0 && human.stat != DEAD) @@ -189,18 +184,6 @@ if(CONFIG_GET(flag/disable_human_mood)) handle_hunger_slowdown(human) - // If we did anything more then just set and throw alerts here I would add bracketing - // But well, it is all we do, so there's not much point bothering with it you get me? - switch(nutrition) - if(NUTRITION_LEVEL_FULL to INFINITY) - human.throw_alert(ALERT_NUTRITION, /atom/movable/screen/alert/fat) - if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL) - human.clear_alert(ALERT_NUTRITION) - if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) - human.throw_alert(ALERT_NUTRITION, /atom/movable/screen/alert/hungry) - if(0 to NUTRITION_LEVEL_STARVING) - human.throw_alert(ALERT_NUTRITION, /atom/movable/screen/alert/starving) - ///for when mood is disabled and hunger should handle slowdowns /obj/item/organ/internal/stomach/proc/handle_hunger_slowdown(mob/living/carbon/human/human) var/hungry = (500 - human.nutrition) / 5 //So overeat would be 100 and default level would be 80 @@ -262,13 +245,16 @@ disgusted.throw_alert(ALERT_DISGUST, /atom/movable/screen/alert/disgusted) disgusted.add_mood_event("disgust", /datum/mood_event/disgusted) +/obj/item/organ/internal/stomach/Insert(mob/living/carbon/receiver, special, movement_flags) + . = ..() + receiver.hud_used?.hunger?.update_appearance() + /obj/item/organ/internal/stomach/Remove(mob/living/carbon/stomach_owner, special, movement_flags) if(ishuman(stomach_owner)) var/mob/living/carbon/human/human_owner = owner human_owner.clear_alert(ALERT_DISGUST) human_owner.clear_mood_event("disgust") - human_owner.clear_alert(ALERT_NUTRITION) - + stomach_owner.hud_used?.hunger?.update_appearance() return ..() /obj/item/organ/internal/stomach/bone diff --git a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm index 68f9d9428a04a..9f268b41c178a 100644 --- a/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm +++ b/code/modules/surgery/organs/internal/stomach/stomach_ethereal.dm @@ -33,7 +33,7 @@ SIGNAL_HANDLER adjust_charge(amount / 3.5) -/obj/item/organ/internal/stomach/ethereal/proc/on_electrocute(datum/source, shock_damage, siemens_coeff = 1, flags = NONE) +/obj/item/organ/internal/stomach/ethereal/proc/on_electrocute(datum/source, shock_damage, shock_source, siemens_coeff = 1, flags = NONE) SIGNAL_HANDLER if(flags & SHOCK_ILLUSION) return diff --git a/code/modules/tgui_input/text.dm b/code/modules/tgui_input/text.dm index f97e0326d58ef..4b3e59a6028c7 100644 --- a/code/modules/tgui_input/text.dm +++ b/code/modules/tgui_input/text.dm @@ -32,9 +32,9 @@ if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input)) if(encode) if(multiline) - return stripped_multiline_input(user, message, title, default, max_length) + return stripped_multiline_input(user, message, title, default, PREVENT_CHARACTER_TRIM_LOSS(max_length)) else - return stripped_input(user, message, title, default, max_length) + return stripped_input(user, message, title, default, PREVENT_CHARACTER_TRIM_LOSS(max_length)) else if(multiline) return input(user, message, title, default) as message|null @@ -162,4 +162,4 @@ /datum/tgui_input_text/proc/set_entry(entry) if(!isnull(entry)) var/converted_entry = encode ? html_encode(entry) : entry - src.entry = trim(converted_entry, max_length) + src.entry = trim(converted_entry, PREVENT_CHARACTER_TRIM_LOSS(max_length)) diff --git a/code/modules/transport/tram/tram_floors.dm b/code/modules/transport/tram/tram_floors.dm index 1e1fad836c3b2..9f0b6907fe9c1 100644 --- a/code/modules/transport/tram/tram_floors.dm +++ b/code/modules/transport/tram/tram_floors.dm @@ -255,7 +255,7 @@ span_notice("You wedge \the [tool] into the tram panel's gap in the frame and start prying...")) if(tool.use_tool(src, user, 1 SECONDS, volume = 50)) to_chat(user, span_notice("The panel pops out of the frame.")) - var/obj/item/stack/thermoplastic/pulled_tile = new() + var/obj/item/stack/thermoplastic/pulled_tile = new floor_tile() pulled_tile.update_integrity(atom_integrity) user.put_in_hands(pulled_tile) qdel(src) @@ -283,8 +283,8 @@ icon = 'icons/obj/tiles.dmi' lefthand_file = 'icons/mob/inhands/items/tiles_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/tiles_righthand.dmi' - icon_state = "tile_textured_white_large" - inhand_icon_state = "tile-neon-glow" + icon_state = "tile_tram_dark" + inhand_icon_state = "tile-tram" color = COLOR_TRAM_BLUE w_class = WEIGHT_CLASS_NORMAL force = 1 @@ -297,7 +297,9 @@ var/tile_type = /obj/structure/thermoplastic /obj/item/stack/thermoplastic/light + icon_state = "tile_tram_light" color = COLOR_TRAM_LIGHT_BLUE + merge_type = /obj/item/stack/thermoplastic/light tile_type = /obj/structure/thermoplastic/light /obj/item/stack/thermoplastic/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) diff --git a/code/modules/transport/transport_module.dm b/code/modules/transport/transport_module.dm index 9fdfefc835cae..7fcfa53fba159 100644 --- a/code/modules/transport/transport_module.dm +++ b/code/modules/transport/transport_module.dm @@ -844,7 +844,6 @@ transport_id = TRANSPORT_TYPE_TRAM transport_controller_type = /datum/transport_controller/linear/tram radial_travel = FALSE - obj_flags = NONE /// Set by the tram control console in late initialize var/travelling = FALSE diff --git a/code/modules/unit_tests/antag_conversion.dm b/code/modules/unit_tests/antag_conversion.dm index 01c05671ef72e..f499922bf8dbe 100644 --- a/code/modules/unit_tests/antag_conversion.dm +++ b/code/modules/unit_tests/antag_conversion.dm @@ -36,7 +36,7 @@ // Success state leader.ClickOn(peasant) - TEST_ASSERT(peasant.IsParalyzed(), "Peasant was not paralyzed after being flashed by the leader.") // Flash paralyze + TEST_ASSERT((peasant.get_timed_status_effect_duration(/datum/status_effect/confusion) > 0), "Peasant was not confused after being flashed by the leader.") // Flash confuse TEST_ASSERT(peasant.IsStun(), "Peasant was not stunned after being converted by the leader.") // Conversion stun TEST_ASSERT(IS_REVOLUTIONARY(peasant), "Peasant did not gain revolution antag datum on conversion.") TEST_ASSERT_EQUAL(length(revolution.members), 2, "Expected revolution to have 2 members after the leader flashes the peasant.") diff --git a/code/modules/unit_tests/bitrunning.dm b/code/modules/unit_tests/bitrunning.dm index 568eeeed8c133..44dd69390afdb 100644 --- a/code/modules/unit_tests/bitrunning.dm +++ b/code/modules/unit_tests/bitrunning.dm @@ -9,7 +9,7 @@ TEST_ASSERT_NOTNULL(vdom.key, "[path] should have a key") TEST_ASSERT_NOTNULL(vdom.map_name, "[path] should have a map name") - if(!length(vdom.extra_loot)) + if(!length(vdom.completion_loot)) continue - TEST_ASSERT_EQUAL(cache.spawn_loot(vdom.extra_loot), TRUE, "[path] didn't spawn loot. Extra loot should be an associative list") + TEST_ASSERT_EQUAL(cache.spawn_loot(vdom.completion_loot), TRUE, "[path] didn't spawn loot. Completion loot should be an associative list") diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index a40723c2f6927..41b5cdf4767e9 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -43,7 +43,6 @@ /mob/living/simple_animal/hostile/asteroid/elite/pandora, /mob/living/simple_animal/hostile/asteroid/polarbear, /mob/living/simple_animal/hostile/asteroid/polarbear/lesser, - /mob/living/simple_animal/hostile/asteroid/wolf, /mob/living/simple_animal/hostile/dark_wizard, /mob/living/simple_animal/hostile/illusion, /mob/living/simple_animal/hostile/illusion/escape, diff --git a/code/modules/unit_tests/siunit.dm b/code/modules/unit_tests/siunit.dm index 3a7a25a98d3ee..7b98db497c8f6 100644 --- a/code/modules/unit_tests/siunit.dm +++ b/code/modules/unit_tests/siunit.dm @@ -12,4 +12,4 @@ TEST_ASSERT_EQUAL(siunit_pressure(999.9e3), "999.9 MPa" , "") TEST_ASSERT_EQUAL(siunit_pressure(999.9e3, 0), "1 GPa", "") TEST_ASSERT_EQUAL(siunit_pressure(1e6), "1 GPa", "") - TEST_ASSERT_EQUAL(siunit_pressure(3e17), "300000 PPa", "") + TEST_ASSERT_EQUAL(siunit_pressure(3e32), "300000 QPa", "") diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm index 127f17e84729b..25c7ef9a2a2a8 100644 --- a/code/modules/uplink/uplink_items/nukeops.dm +++ b/code/modules/uplink/uplink_items/nukeops.dm @@ -68,7 +68,7 @@ /datum/uplink_item/weapon_kits/low_cost/shotgun name = "Bulldog Shotgun Case (Moderate)" - desc = "A fully-loaded semi-automatic drum-fed shotgun, complete with a secondary magazine you can hotswap. The gun has a handy label to explain how. \ + desc = "A fully-loaded 2-round burst fire drum-fed shotgun, complete with a secondary magazine you can hotswap. The gun has a handy label to explain how. \ Compatible with all 12g rounds. Designed for close quarter anti-personnel engagements. Comes with three spare magazines." item = /obj/item/storage/toolbox/guncase/bulldog @@ -744,19 +744,17 @@ cost = 6 purchasable_from = UPLINK_NUKE_OPS | UPLINK_SPY -/datum/uplink_item/implants/nuclear/reviverplus +/datum/uplink_item/implants/nuclear/reviver name = "Reviver Implant" desc = "This implant will attempt to revive and heal you if you lose consciousness. Comes with an autosurgeon." item = /obj/item/autosurgeon/syndicate/reviver cost = 8 - purchasable_from = UPLINK_NUKE_OPS | UPLINK_SPY /datum/uplink_item/implants/nuclear/thermals name = "Thermal Eyes" desc = "These cybernetic eyes will give you thermal vision. Comes with a free autosurgeon." item = /obj/item/autosurgeon/syndicate/thermal_eyes cost = 8 - purchasable_from = UPLINK_NUKE_OPS | UPLINK_SPY /datum/uplink_item/implants/nuclear/implants/xray name = "X-ray Vision Implant" @@ -769,7 +767,6 @@ desc = "This implant will help you get back up on your feet faster after being stunned. Comes with an autosurgeon." item = /obj/item/autosurgeon/syndicate/anti_stun cost = 8 - purchasable_from = UPLINK_NUKE_OPS | UPLINK_SPY // Badass (meme items) diff --git a/code/modules/uplink/uplink_items/spy_unique.dm b/code/modules/uplink/uplink_items/spy_unique.dm index 2f9c4b32576dc..fb3de77481236 100644 --- a/code/modules/uplink/uplink_items/spy_unique.dm +++ b/code/modules/uplink/uplink_items/spy_unique.dm @@ -121,3 +121,18 @@ name = "Syndicate First Medic Kit" desc = "A syndicate tactical combat medkit, but only stocked enough to do basic first aid." item = /obj/item/storage/medkit/tactical_lite + +/datum/uplink_item/implants/spy_unique/antistun + name = /datum/uplink_item/implants/nuclear/antistun::name + desc = /datum/uplink_item/implants/nuclear/antistun::desc + item = /obj/item/autosurgeon/syndicate/anti_stun/single_use + +/datum/uplink_item/implants/spy_unique/reviver + name = /datum/uplink_item/implants/nuclear/reviver::name + desc = /datum/uplink_item/implants/nuclear/reviver::desc + item = /obj/item/autosurgeon/syndicate/reviver/single_use + +/datum/uplink_item/implants/spy_unique/thermals + name = /datum/uplink_item/implants/nuclear/thermals::name + desc = /datum/uplink_item/implants/nuclear/thermals::desc + item = /obj/item/autosurgeon/syndicate/thermal_eyes/single_use diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index f101c5b2f7486..9063617a9b679 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -14,8 +14,12 @@ premium = list() */ +/// List of vending machines that players can restock, so only vending machines that are on station or don't have a unique condition. +GLOBAL_LIST_EMPTY(vending_machines_to_restock) + /// Maximum amount of items in a storage bag that we're transferring items to the vendor from. #define MAX_VENDING_INPUT_AMOUNT 30 +#define CREDITS_DUMP_THRESHOLD 50 /** * # vending record datum * @@ -178,6 +182,8 @@ var/displayed_currency_name = " cr" ///Whether our age check is currently functional var/age_restrictions = TRUE + /// How many credits does this vending machine have? 20% of all sales go to this pool, and are given freely when the machine is restocked, or successfully tilted. Lost on deconstruction. + var/credits_contained = 0 /** * Is this item on station or not * @@ -256,6 +262,7 @@ onstation = FALSE if(circuit) circuit.onstation = onstation //sync up the circuit so the pricing schema is carried over if it's reconstructed. + else if(HAS_TRAIT(SSstation, STATION_TRAIT_VENDING_SHORTAGE)) for (var/datum/data/vending_product/product_record as anything in product_records + coin_records + hidden_records) /** @@ -264,20 +271,26 @@ */ var/max_amount = rand(CEILING(product_record.amount * 0.5, 1), product_record.amount) product_record.amount = rand(0, max_amount) + credits_contained += rand(0, 1) //randomly add a few credits to the machine to make it look like it's been used, proportional to the amount missing. if(tiltable && prob(6)) // 1 in 17 chance to start tilted (as an additional hint to the station trait behind it) INVOKE_ASYNC(src, PROC_REF(tilt), loc) + credits_contained = 0 // If it's tilted, it's been looted, so no credits for you. else if(circuit && (circuit.onstation != onstation)) //check if they're not the same to minimize the amount of edited values. onstation = circuit.onstation //if it was constructed outside mapload, sync the vendor up with the circuit's var so you can't bypass price requirements by moving / reconstructing it off station. + if(onstation && !onstation_override) + AddComponent(/datum/component/payment, 0, SSeconomy.get_dep_account(payment_department), PAYMENT_VENDING) + GLOB.vending_machines_to_restock += src //We need to keep track of the final onstation vending machines so we can keep them restocked. /obj/machinery/vending/Destroy() QDEL_NULL(wires) QDEL_NULL(coin) QDEL_NULL(bill) QDEL_NULL(sec_radio) + GLOB.vending_machines_to_restock -= src return ..() -/obj/machinery/vending/can_speak() - return !shut_up +/obj/machinery/vending/can_speak(allow_mimes) + return is_operational && !shut_up && ..() /obj/machinery/vending/emp_act(severity) . = ..() @@ -357,6 +370,7 @@ continue var/obj/obj_to_dump = new dump_path(loc) + on_dispense(obj_to_dump) step(obj_to_dump, pick(GLOB.alldirs)) found_anything = TRUE dump_amount++ @@ -611,6 +625,24 @@ else //no category found - dump it into standard stock products[record.product_path] = record.amount +/** + * Returns the total amount of items in the vending machine based on the product records and premium records, but not contraband + */ +/obj/machinery/vending/proc/total_loaded_stock() + var/total = 0 + for(var/datum/data/vending_product/record as anything in product_records + coin_records) + total += record.amount + return total + +/** + * Returns the total amount of items in the vending machine based on the product records and premium records, but not contraband + */ +/obj/machinery/vending/proc/total_max_stock() + var/total_max = 0 + for(var/datum/data/vending_product/record as anything in product_records + coin_records) + total_max += record.max_amount + return total_max + /obj/machinery/vending/crowbar_act(mob/living/user, obj/item/attack_item) if(!component_parts) return FALSE @@ -655,7 +687,11 @@ // instantiate canister if needed var/transferred = restock(canister) if(transferred) - to_chat(user, span_notice("You loaded [transferred] items in [src].")) + to_chat(user, span_notice("You loaded [transferred] items in [src][credits_contained > 0 ? ", and are rewarded [credits_contained] credits." : "."]")) + var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR) + cargo_account.adjust_money(round(credits_contained * 0.5), "Vending: Restock") + var/obj/item/holochip/payday = new(src, credits_contained) + try_put_in_hand(payday, user) else to_chat(user, span_warning("There's nothing to restock!")) return @@ -707,7 +743,7 @@ * freebies - number of free items to vend */ /obj/machinery/vending/proc/freebie(freebies) - visible_message(span_notice("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"]!")) + visible_message(span_notice("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"][credits_contained > 0 ? " and some credits" : ""]!")) for(var/i in 1 to freebies) playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3) @@ -719,13 +755,15 @@ if(!dump_path) continue if(record.amount > LAZYLEN(record.returned_products)) //always give out new stuff that costs before free returned stuff, because of the risk getting gibbed involved - new dump_path(get_turf(src)) + var/obj/item/free_stuff = new dump_path(get_turf(src)) + on_dispense(free_stuff) else var/obj/returned_obj_to_dump = LAZYACCESS(record.returned_products, LAZYLEN(record.returned_products)) //first in, last out LAZYREMOVE(record.returned_products, returned_obj_to_dump) returned_obj_to_dump.forceMove(get_turf(src)) record.amount-- break + deploy_credits() /** * Tilts ontop of the atom supplied, if crit is true some extra shit can happen. See [fall_and_crush] for return values. @@ -1206,13 +1244,15 @@ /obj/machinery/vending/ui_data(mob/user) . = list() var/obj/item/card/id/card_used + var/held_cash = 0 if(isliving(user)) var/mob/living/living_user = user card_used = living_user.get_idcard(TRUE) + held_cash = living_user.tally_physical_credits() if(card_used?.registered_account) .["user"] = list() .["user"]["name"] = card_used.registered_account.account_holder - .["user"]["cash"] = fetch_balance_to_use(card_used) + .["user"]["cash"] = fetch_balance_to_use(card_used) + held_cash if(card_used.registered_account.account_job) .["user"]["job"] = card_used.registered_account.account_job.title .["user"]["department"] = card_used.registered_account.account_job.paycheck_department @@ -1333,25 +1373,12 @@ vend_ready = TRUE return if(onstation) + // Here we do additional handing ahead of the payment component's logic, such as age restrictions and additional logging var/obj/item/card/id/card_used + var/mob/living/living_user if(isliving(usr)) - var/mob/living/living_user = usr + living_user = usr card_used = living_user.get_idcard(TRUE) - if(!card_used) - speak("No card found.") - flick(icon_deny,src) - vend_ready = TRUE - return - else if (!card_used.registered_account) - speak("No account found.") - flick(icon_deny,src) - vend_ready = TRUE - return - else if(!card_used.registered_account.account_job) - speak("Departmental accounts have been blacklisted from personal expenses due to embezzlement.") - flick(icon_deny, src) - vend_ready = TRUE - return else if(age_restrictions && item_record.age_restricted && (!card_used.registered_age || card_used.registered_age < AGE_MINOR)) speak("You are not of legal age to purchase [item_record.name].") if(!(usr in GLOB.narcd_underages)) @@ -1365,7 +1392,7 @@ vend_ready = TRUE return - if(!proceed_payment(card_used, item_record, price_to_use)) + if(!proceed_payment(card_used, living_user, item_record, price_to_use)) return if(last_shopper != REF(usr) || purchase_message_cooldown < world.time) @@ -1381,6 +1408,7 @@ var/obj/item/vended_item if(!LAZYLEN(item_record.returned_products)) //always give out free returned stuff first, e.g. to avoid walling a traitor objective in a bag behind paid items vended_item = new item_record.product_path(get_turf(src)) + on_dispense(vended_item) else vended_item = LAZYACCESS(item_record.returned_products, LAZYLEN(item_record.returned_products)) //first in, last out LAZYREMOVE(item_record.returned_products, vended_item) @@ -1395,6 +1423,10 @@ SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[item_record.product_path]")) vend_ready = TRUE +///A proc meant to perform custom behavior on newly dispensed items. +/obj/machinery/vending/proc/on_dispense(obj/item/vended_item) + return + /** * Returns the balance that the vendor will use for proceeding payment. Most vendors would want to use the user's * card's account credits balance. @@ -1407,11 +1439,12 @@ /** * Handles payment processing: discounts, logging, balance change etc. * arguments: - * paying_id_card - the id card that will be billed for the product - * product_to_vend - the product record of the item we're trying to vend - * price_to_use - price of the item we're trying to vend + * paying_id_card - the id card that will be billed for the product. + * mob_paying - the mob that is trying to purchase the item. + * product_to_vend - the product record of the item we're trying to vend. + * price_to_use - price of the item we're trying to vend. */ -/obj/machinery/vending/proc/proceed_payment(obj/item/card/id/paying_id_card, datum/data/vending_product/product_to_vend, price_to_use) +/obj/machinery/vending/proc/proceed_payment(obj/item/card/id/paying_id_card, mob/living/mob_paying, datum/data/vending_product/product_to_vend, price_to_use) var/datum/bank_account/account = paying_id_card.registered_account if(account.account_job && account.account_job.paycheck_department == payment_department) price_to_use = max(round(price_to_use * DEPARTMENT_DISCOUNT), 1) //No longer free, but signifigantly cheaper. @@ -1419,7 +1452,7 @@ price_to_use = product_to_vend.custom_premium_price ? product_to_vend.custom_premium_price : extra_price if(LAZYLEN(product_to_vend.returned_products)) price_to_use = 0 //returned items are free - if(price_to_use && !account.adjust_money(-price_to_use, "Vending: [product_to_vend.name]")) + if(price_to_use && (attempt_charge(src, mob_paying, price_to_use) & COMPONENT_OBJ_CANCEL_CHARGE)) speak("You do not possess the funds to purchase [product_to_vend.name].") flick(icon_deny,src) vend_ready = TRUE @@ -1427,10 +1460,10 @@ //actual payment here var/datum/bank_account/paying_id_account = SSeconomy.get_dep_account(payment_department) if(paying_id_account) - paying_id_account.adjust_money(price_to_use) SSblackbox.record_feedback("amount", "vending_spent", price_to_use) SSeconomy.track_purchase(account, price_to_use, name) log_econ("[price_to_use] credits were inserted into [src] by [account.account_holder] to buy [product_to_vend].") + credits_contained += round(price_to_use * 0.2) return TRUE /obj/machinery/vending/process(seconds_per_tick) @@ -1569,6 +1602,15 @@ tilt(fatty=hit_atom) return ..() +/** Drop credits when the vendor is attacked.*/ +/obj/machinery/vending/proc/deploy_credits() + if(credits_contained <= 0) + return + var/credits_to_remove = min(CREDITS_DUMP_THRESHOLD, round(credits_contained)) + var/obj/item/holochip/holochip = new(loc, credits_to_remove) + credits_contained = max(0, credits_contained - credits_to_remove) + SSblackbox.record_feedback("amount", "vending machine looted", holochip.credits) + /obj/machinery/vending/custom name = "Custom Vendor" icon_state = "custom" diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm index 229b19aeadb99..a043a365046e2 100644 --- a/code/modules/vending/assist.dm +++ b/code/modules/vending/assist.dm @@ -21,6 +21,7 @@ /obj/item/assembly/timer = 2, /obj/item/assembly/voice = 2, /obj/item/stock_parts/cell/high = 1, + /obj/item/market_uplink/blackmarket = 1, ) premium = list( /obj/item/assembly/igniter/condenser = 2, diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm index 59bd06135bb83..2877a40334770 100644 --- a/code/modules/vending/autodrobe.dm +++ b/code/modules/vending/autodrobe.dm @@ -207,6 +207,7 @@ /obj/item/clothing/mask/muzzle = 2, /obj/item/clothing/shoes/ducky_shoes = 1, /obj/item/clothing/shoes/clown_shoes/meown_shoes = 1, + /obj/item/clothing/shoes/clown_shoes/moffers = 1, /obj/item/clothing/suit/costume/judgerobe = 1, /obj/item/clothing/head/costume/lobsterhat = 1, /obj/item/clothing/under/costume/lobster = 1, diff --git a/code/modules/vending/hotdog.dm b/code/modules/vending/hotdog.dm new file mode 100644 index 0000000000000..094beccb41331 --- /dev/null +++ b/code/modules/vending/hotdog.dm @@ -0,0 +1,56 @@ +///A special hotdog vending machine found in the cafeteria at the museum away mission, or during the hotdog holiday. +/obj/machinery/vending/hotdog + name = "\improper Hotdoggo-Vend" + desc = "An outdated hotdog vending machine, its prices stuck to those of 20 or so years ago." + icon_state = "hotdog-vendor" + icon_deny = "hotdog-vendor-deny" + panel_type = "panel17" + product_slogans = "Meatier than ever!;Now with 20% more MSG!;HOTDOGS!;Now Tirizan-friendly!" + product_ads = "Your best and only automatic hotdog dispenser!;Serving you the finest buns since 2469!;Comes in 12 different flavors!" + vend_reply = "Have a scrumptious meal!" + light_mask = "hotdog-vendor-light-mask" + default_price = PAYCHECK_LOWER + product_categories = list( + list( + "name" = "Hotdogs", + "icon" = "hotdog", + "products" = list( + /obj/item/food/hotdog = 8, + /obj/item/food/pigblanket = 4, + /obj/item/food/danish_hotdog = 4, + /obj/item/food/little_hawaii_hotdog = 4, + /obj/item/food/butterdog = 4, + /obj/item/food/plasma_dog_supreme = 2, + ), + ), + list( + name = "Sausages", + "icon" = FA_ICON_BACON, + "products" = list( + /obj/item/food/sausage = 8, + /obj/item/food/tiziran_sausage = 4, + /obj/item/food/fried_blood_sausage = 4, + ), + ), + list( + "name" = "Sauces", + "icon" = FA_ICON_BOWL_FOOD, + "products" = list( + /obj/item/reagent_containers/condiment/pack/ketchup = 4, + /obj/item/reagent_containers/condiment/pack/hotsauce = 4, + /obj/item/reagent_containers/condiment/pack/bbqsauce = 4, + /obj/item/reagent_containers/condiment/pack/soysauce = 4, + /obj/item/reagent_containers/condiment/pack/mayonnaise = 4, + ), + ), + ) + refill_canister = /obj/item/vending_refill/hotdog + +/obj/item/vending_refill/hotdog + machine_name = "\improper Hotdoggo-Vend" + icon_state = "refill_snack" + +/// Cute little thing that sets it apart from the other food vending mahicnes. I mean, you don't find this every day. +/obj/machinery/vending/hotdog/on_dispense(obj/item/vended_item) + if(istype(vended_item, /obj/item/food)) + ADD_TRAIT(vended_item, TRAIT_FOOD_CHEF_MADE, VENDING_MACHINE_TRAIT) diff --git a/code/modules/vending/sustenance.dm b/code/modules/vending/sustenance.dm index d822912149087..a1d11c307277e 100644 --- a/code/modules/vending/sustenance.dm +++ b/code/modules/vending/sustenance.dm @@ -48,7 +48,7 @@ return return ..() -/obj/machinery/vending/sustenance/labor_camp/proceed_payment(obj/item/card/id/paying_id_card, datum/data/vending_product/product_to_vend, price_to_use) +/obj/machinery/vending/sustenance/labor_camp/proceed_payment(obj/item/card/id/paying_id_card, mob/living/mob_paying, datum/data/vending_product/product_to_vend, price_to_use) if(!istype(paying_id_card, /obj/item/card/id/advanced/prisoner)) speak("I don't take bribes! Pay with labor points!") return FALSE diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index 57bb2ed45cbb3..308aea8742032 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -20,16 +20,29 @@ new /obj/item/circuit_component/bci_core, ), SHELL_CAPACITY_SMALL, starting_circuit = circuit) -/obj/item/organ/internal/cyberimp/bci/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/obj/item/organ/internal/cyberimp/bci/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) if (owner) // Otherwise say_dead will be called. // It's intentional that a circuit for a dead person does not speak from the shell. if (owner.stat == DEAD) return - owner.say(message, forced = "circuit speech") - else - return ..() + forced = "circuit speech" + return owner.say(arglist(args)) + + return ..() /obj/item/organ/internal/cyberimp/bci/proc/action_comp_registered(datum/source, obj/item/circuit_component/equipment_action/action_comp) SIGNAL_HANDLER @@ -184,7 +197,7 @@ parent.cell.give(amount) -/obj/item/circuit_component/bci_core/proc/on_electrocute(datum/source, shock_damage, siemens_coefficient, flags) +/obj/item/circuit_component/bci_core/proc/on_electrocute(datum/source, shock_damage, shock_source, siemens_coefficient, flags) SIGNAL_HANDLER if (isnull(parent.cell)) diff --git a/html/changelogs/AutoChangeLog-pr-81510.yml b/html/changelogs/AutoChangeLog-pr-81510.yml new file mode 100644 index 0000000000000..1b30818498941 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81510.yml @@ -0,0 +1,4 @@ +author: "Singul0" +delete-after: True +changes: + - rscadd: "Adds 3 new kits into the syndie-kit spawn pool, 2 for special and another for tactical" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81632.yml b/html/changelogs/AutoChangeLog-pr-81632.yml deleted file mode 100644 index c238f2b55d22b..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81632.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - code_imp: "Splits up the nuclear operative antagonist datum folder." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81683.yml b/html/changelogs/AutoChangeLog-pr-81683.yml deleted file mode 100644 index 3291a02543a4f..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81683.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "xXPawnStarrXx" -delete-after: True -changes: - - bugfix: "makes custom pizzas dairy and vegetable free, unless you choose to add them." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81706.yml b/html/changelogs/AutoChangeLog-pr-81706.yml new file mode 100644 index 0000000000000..01378c729f694 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81706.yml @@ -0,0 +1,4 @@ +author: "SyncIt21" +delete-after: True +changes: + - bugfix: "fixes toolact screentips & balloon alerts for boulder machines & machine frame" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81715.yml b/html/changelogs/AutoChangeLog-pr-81715.yml new file mode 100644 index 0000000000000..e419fdc214d2a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81715.yml @@ -0,0 +1,5 @@ +author: "necromanceranne" +delete-after: True +changes: + - balance: "Bulldog Shotguns now have a 2-round burst fire." + - balance: "Lone Operatives now come with some additional Bulldog Shotgun magazines." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81726.yml b/html/changelogs/AutoChangeLog-pr-81726.yml new file mode 100644 index 0000000000000..672dabd9e563f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81726.yml @@ -0,0 +1,6 @@ +author: "Absolucy" +delete-after: True +changes: + - refactor: "Improved shuttle gibbing code, adding a new resistance flag, `SHUTTLE_CRUSH_PROOF`." + - bugfix: "Immortality revival spectres can no longer be crushed by shuttles." + - rscadd: "The ghost of Poly can no longer be shuttle-crushed, nor can anything incorporeal." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81766.yml b/html/changelogs/AutoChangeLog-pr-81766.yml deleted file mode 100644 index a1a2e78484f37..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81766.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - bugfix: "Fixes some tiles outside the Icebox AI satellite not getting hit by storms." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81771.yml b/html/changelogs/AutoChangeLog-pr-81771.yml new file mode 100644 index 0000000000000..bba60b314ea1e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81771.yml @@ -0,0 +1,4 @@ +author: "necromanceranne" +delete-after: True +changes: + - balance: "Adjusts the values and contents of the Contraband Crate's item loot table." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81790.yml b/html/changelogs/AutoChangeLog-pr-81790.yml new file mode 100644 index 0000000000000..23a405245aaff --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81790.yml @@ -0,0 +1,4 @@ +author: "SyncIt21" +delete-after: True +changes: + - code_imp: "Use a common list for acceptable silo materials for some stuff. Renamed ore category into silo category." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81828.yml b/html/changelogs/AutoChangeLog-pr-81828.yml new file mode 100644 index 0000000000000..97fdfc308e8d4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81828.yml @@ -0,0 +1,7 @@ +author: "Thunder12345" +delete-after: True +changes: + - rscadd: "Added secondary objectives to bitrunning!" + - rscadd: "Pick up encrypted curiosities and return them to the safehouse to claim their contents." + - rscadd: "Glacier Grind has been given a secondary objective, look out for the limited edition hat." + - rscadd: "Bitrunning domains can now be modified during the round by admins." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81843.yml b/html/changelogs/AutoChangeLog-pr-81843.yml new file mode 100644 index 0000000000000..a444dc5bdff10 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81843.yml @@ -0,0 +1,4 @@ +author: "Zergspower" +delete-after: True +changes: + - qol: "Curtains and shower curtains are no longer solid objects that defy common sense" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81871.yml b/html/changelogs/AutoChangeLog-pr-81871.yml new file mode 100644 index 0000000000000..e07c12aa26224 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81871.yml @@ -0,0 +1,4 @@ +author: "Shroopy" +delete-after: True +changes: + - qol: "Uninverted the inverted corner of the Icebox medbay treatment center." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81873.yml b/html/changelogs/AutoChangeLog-pr-81873.yml new file mode 100644 index 0000000000000..5ea8851b8e047 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81873.yml @@ -0,0 +1,5 @@ +author: "Momo8289" +delete-after: True +changes: + - code_imp: "Chem stun reductions are now applied more consistently." + - bugfix: "Chem stun reductions should now more consistently apply to unconsciousness." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81874.yml b/html/changelogs/AutoChangeLog-pr-81874.yml new file mode 100644 index 0000000000000..1e880ca913c9b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81874.yml @@ -0,0 +1,4 @@ +author: "Momo8289" +delete-after: True +changes: + - rscadd: "Wheat and meatwheat can now be worn in the mask slot. Farmers rejoice!" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81901.yml b/html/changelogs/AutoChangeLog-pr-81901.yml new file mode 100644 index 0000000000000..7c22465b017bc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81901.yml @@ -0,0 +1,4 @@ +author: "00-Steven" +delete-after: True +changes: + - bugfix: "When a carbon talks over robotic it uses their voice instead of visible name. Meaning, voice changers work like they do over other comms regardless of face covering." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81902.yml b/html/changelogs/AutoChangeLog-pr-81902.yml new file mode 100644 index 0000000000000..ae3c71c0816c2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81902.yml @@ -0,0 +1,4 @@ +author: "PapaMichael" +delete-after: True +changes: + - bugfix: "Removed erroneous information on some health analyzer's examine text." \ No newline at end of file diff --git a/html/changelogs/archive/2024-03.yml b/html/changelogs/archive/2024-03.yml index 114fb94aa285c..abd3dfc41b715 100644 --- a/html/changelogs/archive/2024-03.yml +++ b/html/changelogs/archive/2024-03.yml @@ -61,3 +61,195 @@ closely. Rhials: - bugfix: Pete can no longer eat vines while dead. +2024-03-03: + Rhials: + - bugfix: Fixes some tiles outside the Icebox AI satellite not getting hit by storms. + - code_imp: Splits up the nuclear operative antagonist datum folder. + xXPawnStarrXx: + - bugfix: makes custom pizzas dairy and vegetable free, unless you choose to add + them. +2024-03-04: + LT3: + - image: Tram frame is now tram girder, because it acts like one + Melbert: + - bugfix: Fixed spy stolen machines not dumping everything that needed to be dumped + Nerev4r: + - qol: Ethereal charging now loops when they're charging (from) APCs or from power + cells! + Singul0: + - rscadd: Adds a new changeling ability, "Darkness Adaptation". Making you more + translucent, especially in darkness and allowing you to see slightly better + in the dark + - balance: The changeling power "Chameleon Skin" has been buffed, Reduces the cost + to 1 and sped up the time it takes to turn invisible + ViktorKoL: + - bugfix: made some heretic descriptions more accurate + - spellcheck: improved english of the heretical eldritch patrons + starrm4nn: + - bugfix: Makes Metastation surgery access more consistent with other maps +2024-03-05: + DaliIsTaken: + - rscadd: added the lightbearer moth set, available in the character setup. + - image: added icons for the lightbearer set; new moth wings, antennae and markings. + LT3: + - bugfix: Tram floor tiles constructed inhand provides 4 instead of 1 + - bugfix: Tram floor tiles provide correct stack item when pulled up + - image: Tram floor tiles have their own inhand icons + - qol: Tram floor tiles available in the engineering protolathe + - bugfix: You can reconstruct deconstructed tram benches + - image: medical and improvised gauze are visibly different from cloth + Rerik007: + - bugfix: fixed the chances of living flesh actions + Rhials: + - bugfix: Some tiny tiny changes to the smoking room ruin to make it a little less + ugly. + ValuedEmployee: + - rscadd: Added new clown shoes "moffers" + - rscadd: Added moffers to the contraband list of the autodrobe + ViktorKoL: + - bugfix: fixed some issues when calculating the duration of moon smile's effects + Zergspower: + - admin: renames ruin names to have an identifier in front of it + - refactor: converts map plate and jump to ruin to tguilist + mc-oofert: + - bugfix: fixes deathmatch baseturfs (you cant crowbar the floor to breach to space) +2024-03-06: + KingkumaArt: + - rscadd: Shiny joke mi-go variant (not xenobio spawnable) + - rscdel: Removed unused mi-go static sprites + - image: Resprited mi-gos to not use plagarized art from CDDA + - image: Also allowed mi-gos to have directional facing instead of always facing + east + Melbert: + - rscadd: The animation that plays when an alert pops up on your screen is different. + - bugfix: Moving "down" as an observer is no longer janky. + - bugfix: All bibles are no longer suspiciously hollow + - bugfix: Extremely Minor Delta Morgue Fixes. See if you can spot them. + Seven: + - rscadd: Lockers and crates now shake when someone is attempting to resist out + of them. + SpaceLove: + - bugfix: Central Command Logistics department noticed the missing items on their + listings for robotics assembly crate. They have updated it! + Wallem: + - bugfix: The cursed coupon now only triggers a cursed event once, rather than infinite + times. + intercepti0n: + - bugfix: Pipe connector no longer appears on a hidden connector. + - bugfix: Re-wrenched atmospherics pipes no longer get extra offset. + - bugfix: All unary devices like injectors, passive vents etc. are centered while + hidden. + - image: Added smooth transition between hidden and visible pipes. +2024-03-07: + 00-Steven: + - bugfix: Alternate job titles such as chef and department security actually get + injected to the manifest as their respective ID trims, instead of being assigned + the job they're based off. + - bugfix: Alternate job titles such as chef and department security actually show + up under the right department on the manifest, instead of no department. + 13spacemen: + - rscdel: Removed Orbit Polling component, all orbit polls now use the Poll Alerts + system + - code_imp: Poll alerts support small border pictures in the chat message + - code_imp: Poll alert alert picture and jump target do not have to be the same + - qol: Slime intelligence potions ask the user for a reason, this will be shown + to ghosts + ArcaneMusic: + - balance: The stock market now fires slower, has stock market events occur more + often, and the stock market has fewer minerals that are available to buy in + a single purchase before restocking. + - balance: Materials sold on the stock market may be protected from being bought + if their prices drop too low, so make sure you watch your prices before they + run the risk of getting shut out! + - balance: Stock blocks now freeze the price of materials for 3 minutes, down from + 5. + - qol: Tweaks to the Galactic Material Market UI, with materials sorted based on + their rarity and a timer to show how long until it updates. + - rscadd: New Stock market events, one locks a material from being purchased, the + other maximizes the value and quantity of a material for sale. + Hatterhat: + - bugfix: Basic mobs no longer have the (unintended) ability to shoot out of containers, + like bluespace body bags. + Higgin: + - balance: personal flashes now Knockdown rather than Paralyze direct targets. + Jacquerel: + - admin: Made it easier for admins to adjust blood brother teams using admin tools. + - bugfix: Correct blood brother conversion logging. + - bugfix: AI-controlled spiders can correctly recognise where they can place webs. + - image: New sprites for most kinds of spider web + Majkl-J: + - bugfix: Prevents polymorphing deleting items by consuming them when transformed + then leaving the polymorph + Melbert: + - refactor: Food hunger bar has been refactored, and moved. Now it sits next to + the moodlet face. + - refactor: Food moodlets now update a lot more snappily. There is now a moodlet + tier between "being fat" and "being normal", to reduce accidentally gorging + yourself to "fatness" tier. + - rscdel: You can't hallucinate being hungry... for now. + - rscadd: Boulder refineries and smelters can refine Golems. + ViktorKoL: + - image: 'added unique icons for spells: caretaker''s refuge, apetra vulnera and + ascended shapechange' + aaaa1023: + - bugfix: Revenants can now again emag Medibots, Cleanbots, and Hygienebots. + san7890: + - qol: If your OOC message gets eaten due to some weird circumstance in how your + message is handled, it will feed the applicable message back to you so you can + copy-paste and try to send it again. +2024-03-08: + 2whatever2: + - bugfix: Tackle and wellcheers code for sanity values now function. + ArcaneMusic: + - rscadd: Vending machines now track how many credits have been spent on them, and + when restocked will pay out that saved portion to the restocker, with a 50% + match going to the cargo department. + - rscadd: Adds the restock tracker app, an NTOS app that tracks how well stocked + the station's vending machine units are at a glance as well as how much is contained + in each. + - refactor: Vending machines now use the payment component for money handling behavior, + meaning it will now accept held or pulled coins/cash/credits + - qol: Attacking vending machines can drop a portion of it's stored credits, at + the usual expected danger. + - balance: Tweaked the cost of various restock modules up and down. + - qol: Restock modules can now be sold for 50 credits. + Ben10Omintrix: + - bugfix: fixes ore vent spawned wolves being untammable + JohnFulpWillard: + - image: The minigames icon now has an icon for the deathmatch minigamee + Melbert: + - rscadd: You can talk via plushies, action figures, and toy mechs via `.l` or `.r`, + same as a ventriloquist dummy or decapitated dead + - rscdel: Talking via a plushie, action figure, toy mech, ventriloqiest dummy, decapitated + head, or marionette won't transmit over open mic comms + - refactor: Some say refactor and code cleanup. Vending machines now properly respect + being shut up. Report any oddities. + ValuedEmployee: + - sound: Added the new moffers sound effect and made moffers use it instead + cnleth: + - qol: Tramstation botany now has roundstart watering cans and syringes + intercepti0n: + - qol: ID cards in modular computers can now be swapped. + starrm4nn: + - bugfix: MetaStation Pharmacy is no longer accessible with general medical access, + Also changes the Chemistry and Pharmacy airlocks into medical ones. +2024-03-09: + 00-Steven: + - bugfix: Plexagon Access Management actually updates the shown template list on + authentication, avoiding needing to refresh/reopen/somesuch the program manually. + Drag: + - rscadd: Adds the Shark and Shork costume. Blahaj lovers rejoice! + Ghommie: + - bugfix: Fixed the tgui text input trimming the last character of the input if + it hits the maximum length. + - bugfix: This also fixes the PIN pad leading to the right wing of the museum away + mission. + Pickle-Coding: + - qol: Extended the metric prefixes. + mc-oofert: + - rscdel: Removed a nanomachine pizza from the deathmatch meat tower map that allowed + you to become a borg + necromanceranne: + - bugfix: Spies no longer have access to infinite use autosurgeons. + starrm4nn: + - qol: EVA can be given now in common access slots for non-command crewmembers. diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index d0734355c5e3f..50fe3e3b11af9 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/web.dmi b/icons/effects/web.dmi new file mode 100644 index 0000000000000..e21eb4d6ec34d Binary files /dev/null and b/icons/effects/web.dmi differ diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi index 7aa32c94b8e2a..e1c5db84d22ae 100644 Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ diff --git a/icons/hud/screen_gen.dmi b/icons/hud/screen_gen.dmi index 608be00b2e1fb..2f4dd60b5d7ce 100644 Binary files a/icons/hud/screen_gen.dmi and b/icons/hud/screen_gen.dmi differ diff --git a/icons/hud/screen_ghost.dmi b/icons/hud/screen_ghost.dmi index b58680f025df4..13255a2a16890 100644 Binary files a/icons/hud/screen_ghost.dmi and b/icons/hud/screen_ghost.dmi differ diff --git a/icons/mob/actions/actions_animal.dmi b/icons/mob/actions/actions_animal.dmi index 64b1c700f414c..c13290716b75c 100644 Binary files a/icons/mob/actions/actions_animal.dmi and b/icons/mob/actions/actions_animal.dmi differ diff --git a/icons/mob/actions/actions_changeling.dmi b/icons/mob/actions/actions_changeling.dmi index 25a4e10aa578b..73aac72deb57d 100644 Binary files a/icons/mob/actions/actions_changeling.dmi and b/icons/mob/actions/actions_changeling.dmi differ diff --git a/icons/mob/actions/actions_ecult.dmi b/icons/mob/actions/actions_ecult.dmi index ac7575d279b9e..c7744749abb97 100644 Binary files a/icons/mob/actions/actions_ecult.dmi and b/icons/mob/actions/actions_ecult.dmi differ diff --git a/icons/mob/clothing/feet.dmi b/icons/mob/clothing/feet.dmi index 5bd3005ab8e58..612d82302e714 100644 Binary files a/icons/mob/clothing/feet.dmi and b/icons/mob/clothing/feet.dmi differ diff --git a/icons/mob/clothing/head/costume.dmi b/icons/mob/clothing/head/costume.dmi index 06de36df19aac..4bce495a29143 100644 Binary files a/icons/mob/clothing/head/costume.dmi and b/icons/mob/clothing/head/costume.dmi differ diff --git a/icons/mob/clothing/head/hydroponics.dmi b/icons/mob/clothing/head/hydroponics.dmi index b710efc3d7d90..5e2a72a819f10 100644 Binary files a/icons/mob/clothing/head/hydroponics.dmi and b/icons/mob/clothing/head/hydroponics.dmi differ diff --git a/icons/mob/clothing/mask.dmi b/icons/mob/clothing/mask.dmi index db1259996c867..3a213d5275ccd 100644 Binary files a/icons/mob/clothing/mask.dmi and b/icons/mob/clothing/mask.dmi differ diff --git a/icons/mob/clothing/suits/costume.dmi b/icons/mob/clothing/suits/costume.dmi index 3cd5770fb9aee..3dfaf138da8dd 100644 Binary files a/icons/mob/clothing/suits/costume.dmi and b/icons/mob/clothing/suits/costume.dmi differ diff --git a/icons/mob/human/species/moth/moth_antennae.dmi b/icons/mob/human/species/moth/moth_antennae.dmi index 9a4d237cc3621..a40013e93576f 100644 Binary files a/icons/mob/human/species/moth/moth_antennae.dmi and b/icons/mob/human/species/moth/moth_antennae.dmi differ diff --git a/icons/mob/human/species/moth/moth_markings.dmi b/icons/mob/human/species/moth/moth_markings.dmi index 2429b0aa12dfe..a6ebc2cedb517 100644 Binary files a/icons/mob/human/species/moth/moth_markings.dmi and b/icons/mob/human/species/moth/moth_markings.dmi differ diff --git a/icons/mob/human/species/moth/moth_wings.dmi b/icons/mob/human/species/moth/moth_wings.dmi index 65b8fba38d9b9..91c0d8b2b2cd1 100644 Binary files a/icons/mob/human/species/moth/moth_wings.dmi and b/icons/mob/human/species/moth/moth_wings.dmi differ diff --git a/icons/mob/inhands/clothing/suits_lefthand.dmi b/icons/mob/inhands/clothing/suits_lefthand.dmi index 8b9fa5256a932..a43756d743a9b 100644 Binary files a/icons/mob/inhands/clothing/suits_lefthand.dmi and b/icons/mob/inhands/clothing/suits_lefthand.dmi differ diff --git a/icons/mob/inhands/clothing/suits_righthand.dmi b/icons/mob/inhands/clothing/suits_righthand.dmi index c88f4d224444f..da837fdd8570e 100644 Binary files a/icons/mob/inhands/clothing/suits_righthand.dmi and b/icons/mob/inhands/clothing/suits_righthand.dmi differ diff --git a/icons/mob/inhands/equipment/briefcase_lefthand.dmi b/icons/mob/inhands/equipment/briefcase_lefthand.dmi index 5781ecdc992a7..9eb7d24a3ce12 100644 Binary files a/icons/mob/inhands/equipment/briefcase_lefthand.dmi and b/icons/mob/inhands/equipment/briefcase_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/briefcase_righthand.dmi b/icons/mob/inhands/equipment/briefcase_righthand.dmi index d48529a54e9ee..06ed8e2c8eca2 100644 Binary files a/icons/mob/inhands/equipment/briefcase_righthand.dmi and b/icons/mob/inhands/equipment/briefcase_righthand.dmi differ diff --git a/icons/mob/inhands/items/tiles_lefthand.dmi b/icons/mob/inhands/items/tiles_lefthand.dmi index b73ded0c82c54..6bbe621db829a 100644 Binary files a/icons/mob/inhands/items/tiles_lefthand.dmi and b/icons/mob/inhands/items/tiles_lefthand.dmi differ diff --git a/icons/mob/inhands/items/tiles_righthand.dmi b/icons/mob/inhands/items/tiles_righthand.dmi index 1d668c01f070c..4d1d5073c98d6 100644 Binary files a/icons/mob/inhands/items/tiles_righthand.dmi and b/icons/mob/inhands/items/tiles_righthand.dmi differ diff --git a/icons/mob/simple/animal.dmi b/icons/mob/simple/animal.dmi index 7fcf0e9d65e79..82943b798a941 100644 Binary files a/icons/mob/simple/animal.dmi and b/icons/mob/simple/animal.dmi differ diff --git a/icons/obj/clothing/head/costume.dmi b/icons/obj/clothing/head/costume.dmi index 3cfbd3d21ef4f..e72c278b6eb37 100644 Binary files a/icons/obj/clothing/head/costume.dmi and b/icons/obj/clothing/head/costume.dmi differ diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi index 0acc23d128217..37f561bb44b13 100644 Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ diff --git a/icons/obj/clothing/suits/costume.dmi b/icons/obj/clothing/suits/costume.dmi index ad68aea553f7f..f87a74c263f76 100644 Binary files a/icons/obj/clothing/suits/costume.dmi and b/icons/obj/clothing/suits/costume.dmi differ diff --git a/icons/obj/fluff/general.dmi b/icons/obj/fluff/general.dmi index 1aa7ae5c89857..f99cbaabc9a1d 100644 Binary files a/icons/obj/fluff/general.dmi and b/icons/obj/fluff/general.dmi differ diff --git a/icons/obj/fluff/puzzle_small.dmi b/icons/obj/fluff/puzzle_small.dmi index 1d94c0c8034ca..2f6ff9ebe3232 100644 Binary files a/icons/obj/fluff/puzzle_small.dmi and b/icons/obj/fluff/puzzle_small.dmi differ diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi index d7e3d73f861ca..aea93b956fc3b 100644 Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ diff --git a/icons/obj/machines/modular_console.dmi b/icons/obj/machines/modular_console.dmi index 2677dbb71220a..7b370a767852c 100644 Binary files a/icons/obj/machines/modular_console.dmi and b/icons/obj/machines/modular_console.dmi differ diff --git a/icons/obj/machines/vending.dmi b/icons/obj/machines/vending.dmi index a5db6caa6e2ba..bf3c42bde16a1 100644 Binary files a/icons/obj/machines/vending.dmi and b/icons/obj/machines/vending.dmi differ diff --git a/icons/obj/machines/wallmounts.dmi b/icons/obj/machines/wallmounts.dmi index 12a9c8e418f08..e849746eb0a66 100644 Binary files a/icons/obj/machines/wallmounts.dmi and b/icons/obj/machines/wallmounts.dmi differ diff --git a/icons/obj/medical/stack_medical.dmi b/icons/obj/medical/stack_medical.dmi index c4ec905786c69..b47cff516f211 100644 Binary files a/icons/obj/medical/stack_medical.dmi and b/icons/obj/medical/stack_medical.dmi differ diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi index c8ad438d1a38f..1accc56f4c188 100644 Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ diff --git a/icons/obj/modular_pda.dmi b/icons/obj/modular_pda.dmi index 75553403ee2f8..594379503aaa9 100644 Binary files a/icons/obj/modular_pda.dmi and b/icons/obj/modular_pda.dmi differ diff --git a/icons/obj/pipes_n_cables/!pipe_gas_overlays.dmi b/icons/obj/pipes_n_cables/!pipe_gas_overlays.dmi index 0262adcaeb241..0ffed70fa5cc1 100644 Binary files a/icons/obj/pipes_n_cables/!pipe_gas_overlays.dmi and b/icons/obj/pipes_n_cables/!pipe_gas_overlays.dmi differ diff --git a/icons/obj/pipes_n_cables/!pipes_bitmask.dmi b/icons/obj/pipes_n_cables/!pipes_bitmask.dmi index 97643036fbe3b..e8bf7af5973a0 100644 Binary files a/icons/obj/pipes_n_cables/!pipes_bitmask.dmi and b/icons/obj/pipes_n_cables/!pipes_bitmask.dmi differ diff --git a/icons/obj/pipes_n_cables/pipe_template_pieces.dmi b/icons/obj/pipes_n_cables/pipe_template_pieces.dmi index d0d2f7ff7bb80..2316d7f3d9614 100644 Binary files a/icons/obj/pipes_n_cables/pipe_template_pieces.dmi and b/icons/obj/pipes_n_cables/pipe_template_pieces.dmi differ diff --git a/icons/obj/poster.dmi b/icons/obj/poster.dmi index c1120d0be0b7e..8193b38e1f21c 100644 Binary files a/icons/obj/poster.dmi and b/icons/obj/poster.dmi differ diff --git a/icons/obj/smooth_structures/stickyweb.dmi b/icons/obj/smooth_structures/stickyweb.dmi new file mode 100644 index 0000000000000..2c445260bd267 Binary files /dev/null and b/icons/obj/smooth_structures/stickyweb.dmi differ diff --git a/icons/obj/smooth_structures/stickyweb.png b/icons/obj/smooth_structures/stickyweb.png new file mode 100644 index 0000000000000..ae53aaa53b2b5 Binary files /dev/null and b/icons/obj/smooth_structures/stickyweb.png differ diff --git a/icons/obj/smooth_structures/stickyweb.png.toml b/icons/obj/smooth_structures/stickyweb.png.toml new file mode 100644 index 0000000000000..cf7f5ce339f0b --- /dev/null +++ b/icons/obj/smooth_structures/stickyweb.png.toml @@ -0,0 +1,14 @@ +output_name = "stickyweb" +template = "bitmask/diagonal_32x32.toml" + +[icon_size] +x = 50 +y = 50 + +[output_icon_size] +x = 50 +y = 50 + +[cut_pos] +x = 25 +y = 25 diff --git a/icons/obj/smooth_structures/stickyweb_rotated.dmi b/icons/obj/smooth_structures/stickyweb_rotated.dmi new file mode 100644 index 0000000000000..11d3e5546ae34 Binary files /dev/null and b/icons/obj/smooth_structures/stickyweb_rotated.dmi differ diff --git a/icons/obj/smooth_structures/stickyweb_rotated.png b/icons/obj/smooth_structures/stickyweb_rotated.png new file mode 100644 index 0000000000000..6c5413ab792e9 Binary files /dev/null and b/icons/obj/smooth_structures/stickyweb_rotated.png differ diff --git a/icons/obj/smooth_structures/stickyweb_rotated.png.toml b/icons/obj/smooth_structures/stickyweb_rotated.png.toml new file mode 100644 index 0000000000000..61c615585e1c5 --- /dev/null +++ b/icons/obj/smooth_structures/stickyweb_rotated.png.toml @@ -0,0 +1,14 @@ +output_name = "stickyweb_rotated" +template = "bitmask/diagonal_32x32.toml" + +[icon_size] +x = 50 +y = 50 + +[output_icon_size] +x = 50 +y = 50 + +[cut_pos] +x = 25 +y = 25 diff --git a/icons/obj/smooth_structures/stickyweb_spikes.dmi b/icons/obj/smooth_structures/stickyweb_spikes.dmi new file mode 100644 index 0000000000000..b3dcfcc83f944 Binary files /dev/null and b/icons/obj/smooth_structures/stickyweb_spikes.dmi differ diff --git a/icons/obj/smooth_structures/stickyweb_spikes.png b/icons/obj/smooth_structures/stickyweb_spikes.png new file mode 100644 index 0000000000000..98832695f67f5 Binary files /dev/null and b/icons/obj/smooth_structures/stickyweb_spikes.png differ diff --git a/icons/obj/smooth_structures/stickyweb_spikes.png.toml b/icons/obj/smooth_structures/stickyweb_spikes.png.toml new file mode 100644 index 0000000000000..ebd1260cfcf64 --- /dev/null +++ b/icons/obj/smooth_structures/stickyweb_spikes.png.toml @@ -0,0 +1,14 @@ +output_name = "stickyweb_spikes" +template = "bitmask/diagonal_32x32.toml" + +[icon_size] +x = 50 +y = 50 + +[output_icon_size] +x = 50 +y = 50 + +[cut_pos] +x = 25 +y = 25 diff --git a/icons/obj/smooth_structures/webwall.dmi b/icons/obj/smooth_structures/webwall.dmi new file mode 100644 index 0000000000000..e2308526cc96f Binary files /dev/null and b/icons/obj/smooth_structures/webwall.dmi differ diff --git a/icons/obj/smooth_structures/webwall.png b/icons/obj/smooth_structures/webwall.png new file mode 100644 index 0000000000000..207978b8aaf47 Binary files /dev/null and b/icons/obj/smooth_structures/webwall.png differ diff --git a/icons/obj/smooth_structures/webwall.png.toml b/icons/obj/smooth_structures/webwall.png.toml new file mode 100644 index 0000000000000..3f91f5f62e732 --- /dev/null +++ b/icons/obj/smooth_structures/webwall.png.toml @@ -0,0 +1,2 @@ +output_name = "webwall" +template = "bitmask/diagonal_32x32.toml" diff --git a/icons/obj/smooth_structures/webwall_dark.dmi b/icons/obj/smooth_structures/webwall_dark.dmi new file mode 100644 index 0000000000000..d3863a818144e Binary files /dev/null and b/icons/obj/smooth_structures/webwall_dark.dmi differ diff --git a/icons/obj/smooth_structures/webwall_dark.png b/icons/obj/smooth_structures/webwall_dark.png new file mode 100644 index 0000000000000..df36c5108bda0 Binary files /dev/null and b/icons/obj/smooth_structures/webwall_dark.png differ diff --git a/icons/obj/smooth_structures/webwall_dark.png.toml b/icons/obj/smooth_structures/webwall_dark.png.toml new file mode 100644 index 0000000000000..4b3b155f12614 --- /dev/null +++ b/icons/obj/smooth_structures/webwall_dark.png.toml @@ -0,0 +1,2 @@ +output_name = "webwall_dark" +template = "bitmask/diagonal_32x32.toml" diff --git a/icons/obj/smooth_structures/webwall_reflector.dmi b/icons/obj/smooth_structures/webwall_reflector.dmi new file mode 100644 index 0000000000000..04547349d53cd Binary files /dev/null and b/icons/obj/smooth_structures/webwall_reflector.dmi differ diff --git a/icons/obj/smooth_structures/webwall_reflector.png b/icons/obj/smooth_structures/webwall_reflector.png new file mode 100644 index 0000000000000..881cb24fc0dad Binary files /dev/null and b/icons/obj/smooth_structures/webwall_reflector.png differ diff --git a/icons/obj/smooth_structures/webwall_reflector.png.toml b/icons/obj/smooth_structures/webwall_reflector.png.toml new file mode 100644 index 0000000000000..c47c554f8dabb --- /dev/null +++ b/icons/obj/smooth_structures/webwall_reflector.png.toml @@ -0,0 +1,2 @@ +output_name = "webwall_reflector" +template = "bitmask/diagonal_32x32.toml" diff --git a/icons/obj/storage/case.dmi b/icons/obj/storage/case.dmi index 0320ce881f90e..a47c86eea9fb3 100644 Binary files a/icons/obj/storage/case.dmi and b/icons/obj/storage/case.dmi differ diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi index 1e6a2ba68724e..50861b248d530 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 fdddb793362a6..4e26f75aa99d1 100644 Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ diff --git a/icons/obj/tram/tram_wall.dmi b/icons/obj/tram/tram_wall.dmi deleted file mode 100644 index 42448dc77d318..0000000000000 Binary files a/icons/obj/tram/tram_wall.dmi and /dev/null differ diff --git a/sound/attributions.txt b/sound/attributions.txt index 82486a5735da0..c81aa3e664b67 100644 --- a/sound/attributions.txt +++ b/sound/attributions.txt @@ -117,6 +117,9 @@ https://freesound.org/people/humanoide9000/sounds/330293/ reel1.ogg, reel2.ogg, reel3.ogg, reel4.ogg and reel5.ogg adapted from pixabay. Free for use under the Pixabay Content License (https://pixabay.com/service/license-summary/): https://pixabay.com/sound-effects/reel-78063/ +rattle1.ogg, rattle2.ogg and rattle3.ogg adapted from pixabay. Free for use under the Pixabay Content License (https://pixabay.com/service/license-summary/): +https://pixabay.com/sound-effects/chain-6073/ + throw.ogg, throwhard.ogg and throwsoft.ogg (Royalty-Free and Copyright-Free) are adapted from Jam FX, SmartSound FX and Epic Stock Media in : https://uppbeat.io/sfx/whoosh-swift-cut/7727/23617 https://uppbeat.io/sfx/whoosh-air-punch/114/1168 diff --git a/sound/effects/footstep/moffstep01.ogg b/sound/effects/footstep/moffstep01.ogg new file mode 100644 index 0000000000000..6350cb057bf0b Binary files /dev/null and b/sound/effects/footstep/moffstep01.ogg differ diff --git a/sound/items/rattle1.ogg b/sound/items/rattle1.ogg new file mode 100644 index 0000000000000..71c4110fafe46 Binary files /dev/null and b/sound/items/rattle1.ogg differ diff --git a/sound/items/rattle2.ogg b/sound/items/rattle2.ogg new file mode 100644 index 0000000000000..30f0e2d85ea93 Binary files /dev/null and b/sound/items/rattle2.ogg differ diff --git a/sound/items/rattle3.ogg b/sound/items/rattle3.ogg new file mode 100644 index 0000000000000..ef1cfc6bf6b6f Binary files /dev/null and b/sound/items/rattle3.ogg differ diff --git a/tgstation.dme b/tgstation.dme index a4c8d17da602f..b5fbfe44b585f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -618,6 +618,7 @@ #include "code\controllers\subsystem\atoms.dm" #include "code\controllers\subsystem\augury.dm" #include "code\controllers\subsystem\ban_cache.dm" +#include "code\controllers\subsystem\bitrunning.dm" #include "code\controllers\subsystem\blackbox.dm" #include "code\controllers\subsystem\blackmarket.dm" #include "code\controllers\subsystem\chat.dm" @@ -725,6 +726,7 @@ #include "code\controllers\subsystem\persistence\custom_outfits.dm" #include "code\controllers\subsystem\persistence\engravings.dm" #include "code\controllers\subsystem\persistence\photo_albums.dm" +#include "code\controllers\subsystem\persistence\piggy_banks.dm" #include "code\controllers\subsystem\persistence\recipes.dm" #include "code\controllers\subsystem\persistence\scars.dm" #include "code\controllers\subsystem\persistence\tattoos.dm" @@ -1127,7 +1129,6 @@ #include "code\datums\components\omen.dm" #include "code\datums\components\on_hit_effect.dm" #include "code\datums\components\onwear_mood.dm" -#include "code\datums\components\orbit_poll.dm" #include "code\datums\components\orbiter.dm" #include "code\datums\components\overlay_lighting.dm" #include "code\datums\components\palette.dm" @@ -1368,6 +1369,7 @@ #include "code\datums\elements\bugkiller_reagent.dm" #include "code\datums\elements\bump_click.dm" #include "code\datums\elements\can_barricade.dm" +#include "code\datums\elements\can_shatter.dm" #include "code\datums\elements\caseless.dm" #include "code\datums\elements\chemical_transfer.dm" #include "code\datums\elements\chewable.dm" @@ -1461,7 +1463,6 @@ #include "code\datums\elements\rust.dm" #include "code\datums\elements\selfknockback.dm" #include "code\datums\elements\series.dm" -#include "code\datums\elements\shatters_when_thrown.dm" #include "code\datums\elements\sideway_movement.dm" #include "code\datums\elements\simple_flying.dm" #include "code\datums\elements\skill_reward.dm" @@ -1479,6 +1480,7 @@ #include "code\datums\elements\tenacious.dm" #include "code\datums\elements\tiny_mob_hunter.dm" #include "code\datums\elements\tool_flash.dm" +#include "code\datums\elements\toy_talk.dm" #include "code\datums\elements\turf_transparency.dm" #include "code\datums\elements\undertile.dm" #include "code\datums\elements\unfriend_attacker.dm" @@ -2214,6 +2216,7 @@ #include "code\game\objects\items\botpad_remote.dm" #include "code\game\objects\items\boxcutter.dm" #include "code\game\objects\items\broom.dm" +#include "code\game\objects\items\busts_and_figurines.dm" #include "code\game\objects\items\cardboard_cutouts.dm" #include "code\game\objects\items\cards_ids.dm" #include "code\game\objects\items\chainsaw.dm" @@ -2268,6 +2271,7 @@ #include "code\game\objects\items\paint.dm" #include "code\game\objects\items\paiwire.dm" #include "code\game\objects\items\pet_carrier.dm" +#include "code\game\objects\items\piggy_bank.dm" #include "code\game\objects\items\pillow.dm" #include "code\game\objects\items\pinpointer.dm" #include "code\game\objects\items\pitchfork.dm" @@ -2954,6 +2958,7 @@ #include "code\modules\antagonists\changeling\powers\augmented_eyesight.dm" #include "code\modules\antagonists\changeling\powers\biodegrade.dm" #include "code\modules\antagonists\changeling\powers\chameleon_skin.dm" +#include "code\modules\antagonists\changeling\powers\darkness_adaptation.dm" #include "code\modules\antagonists\changeling\powers\defib_grasp.dm" #include "code\modules\antagonists\changeling\powers\digitalcamo.dm" #include "code\modules\antagonists\changeling\powers\fakedeath.dm" @@ -3423,6 +3428,7 @@ #include "code\modules\bitrunning\objects\hololadder.dm" #include "code\modules\bitrunning\objects\host_monitor.dm" #include "code\modules\bitrunning\objects\landmarks.dm" +#include "code\modules\bitrunning\objects\loot_box.dm" #include "code\modules\bitrunning\objects\loot_crate.dm" #include "code\modules\bitrunning\objects\netpod.dm" #include "code\modules\bitrunning\objects\quantum_console.dm" @@ -4994,7 +5000,6 @@ #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\curse_blob.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\mining_mobs.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\polarbear.dm" -#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\wolf.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\elites\elite.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\elites\goliath_broodmother.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\elites\herald.dm" @@ -5100,6 +5105,7 @@ #include "code\modules\modular_computers\file_system\programs\powermonitor.dm" #include "code\modules\modular_computers\file_system\programs\radar.dm" #include "code\modules\modular_computers\file_system\programs\records.dm" +#include "code\modules\modular_computers\file_system\programs\restock_tracker.dm" #include "code\modules\modular_computers\file_system\programs\robocontrol.dm" #include "code\modules\modular_computers\file_system\programs\robotact.dm" #include "code\modules\modular_computers\file_system\programs\secureye.dm" @@ -5942,6 +5948,7 @@ #include "code\modules\vending\engineering.dm" #include "code\modules\vending\engivend.dm" #include "code\modules\vending\games.dm" +#include "code\modules\vending\hotdog.dm" #include "code\modules\vending\liberation.dm" #include "code\modules\vending\liberation_toy.dm" #include "code\modules\vending\magivend.dm" diff --git a/tgui/packages/tgui/components/Dropdown.tsx b/tgui/packages/tgui/components/Dropdown.tsx index 4fabfbf0bbf2d..a103a7466d3f5 100644 --- a/tgui/packages/tgui/components/Dropdown.tsx +++ b/tgui/packages/tgui/components/Dropdown.tsx @@ -97,9 +97,9 @@ export function Dropdown(props: Props) { let newIndex = selectedIndex; if (direction === 'next') { - newIndex = selectedIndex === endIndex ? startIndex : selectedIndex++; + newIndex = selectedIndex === endIndex ? startIndex : ++selectedIndex; } else { - newIndex = selectedIndex === startIndex ? endIndex : selectedIndex--; + newIndex = selectedIndex === startIndex ? endIndex : --selectedIndex; } onSelected?.(getOptionValue(options[newIndex])); diff --git a/tgui/packages/tgui/interfaces/MatMarket.tsx b/tgui/packages/tgui/interfaces/MatMarket.tsx index 41b25dedb0021..1f6d69c8d69e3 100644 --- a/tgui/packages/tgui/interfaces/MatMarket.tsx +++ b/tgui/packages/tgui/interfaces/MatMarket.tsx @@ -1,14 +1,23 @@ +import { sortBy } from 'common/collections'; import { BooleanLike } from 'common/react'; import { toTitleCase } from 'common/string'; import { useBackend } from '../backend'; -import { Button, Modal, Section, Stack } from '../components'; +import { + Button, + Collapsible, + Modal, + NoticeBox, + Section, + Stack, +} from '../components'; import { formatMoney } from '../format'; import { Window } from '../layouts'; type Material = { name: string; quantity: number; + rarity: number; trend: string; price: number; threshold: number; @@ -24,6 +33,7 @@ type Data = { materials: Material[]; catastrophe: BooleanLike; CARGO_CRATE_VALUE: number; + updateTime: number; }; export const MatMarket = (props) => { @@ -66,24 +76,35 @@ export const MatMarket = (props) => { ) } > - Buy orders for material sheets placed here will be ordered on the next - cargo shipment. -

- To sell materials, please insert sheets or similar stacks of - materials. All minerals sold on the market directly are subject to an - 20% market fee. To prevent market manipulation, all registered traders - can buy a total of 10 full stacks of materials at a time. -

- All new purchases will include the cost of the shipped crate, - which may be recycled afterwards. + + + Buy orders for material sheets placed here will be ordered on the + next cargo shipment. +

+ To sell materials, please insert sheets or similar stacks of + materials. All minerals sold on the market directly are subject to + an 20% market fee. To prevent market manipulation, all registered + traders can buy a total of 10 full stacks of materials at a time. +

+ All new purchases will include the cost of the shipped crate, + which may be recycled afterwards. +
+
- + Current Credit Balance: {formatMoney(creditBalance)} cr. - + Current Order Cost: {formatMoney(orderBalance)} cr. + 150 ? 'green' : '#ad7526'} + > + {Math.round(data.updateTime / 10)} seconds until next + update +
- {materials.map((material, i) => ( -
- - - - - {toTitleCase(material.name)} - - - - Trading at {formatMoney(material.price)} cr. - - {material.price < material.threshold ? ( - - Material price critical! -
Trading temporarily suspended. + {sortBy((tempmat: Material) => tempmat.rarity)(materials).map( + (material, i) => ( +
+ + + + + {toTitleCase(material.name)} - ) : ( - - {material.quantity || 'Zero'} sheets of{' '} - {material.name} trading.{' '} - {material.requested || 'Zero'} sheets ordered. + + + Trading at {formatMoney(material.price)} cr. - )} + {material.price < material.threshold ? ( + + Material price critical! +
Trading temporarily suspended. +
+ ) : ( + + {material.quantity || 'Zero'} sheets of{' '} + {material.name} trading.{' '} + {material.requested || 'Zero'} sheets ordered. + + )} - + {toTitleCase(material.name)} is trending{' '} + {material.trend}. + +
+
+ + - - - - - - {material.requested > 0 && ( - x {material.requested} - )} -
-
- ))} + Buy 1 + + + + + +
+ {material.requested > 0 && ( + x {material.requested} + )} +
+
+ ), + )} ); diff --git a/tgui/packages/tgui/interfaces/NtosRestock.tsx b/tgui/packages/tgui/interfaces/NtosRestock.tsx new file mode 100644 index 0000000000000..055dfda6e4e04 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosRestock.tsx @@ -0,0 +1,12 @@ +import { NtosWindow } from '../layouts'; +import { RestockTracker } from './RestockTracker'; + +export const NtosRestock = (props) => { + return ( + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/QuantumConsole.tsx b/tgui/packages/tgui/interfaces/QuantumConsole.tsx index dca8f2dbf1554..7d64056307cd6 100644 --- a/tgui/packages/tgui/interfaces/QuantumConsole.tsx +++ b/tgui/packages/tgui/interfaces/QuantumConsole.tsx @@ -50,6 +50,7 @@ type Domain = { difficulty: number; id: string; is_modular: BooleanLike; + has_secondary_objectives: BooleanLike; name: string; reward: number | string; }; @@ -228,7 +229,16 @@ const AccessView = (props) => { const DomainEntry = (props: DomainEntryProps) => { const { - domain: { cost, desc, difficulty, id, is_modular, name, reward }, + domain: { + cost, + desc, + difficulty, + id, + is_modular, + has_secondary_objectives, + name, + reward, + }, } = props; const { act, data } = useBackend(); if (!isConnected(data)) { @@ -268,6 +278,9 @@ const DomainEntry = (props: DomainEntryProps) => { <> {name} {!!is_modular && name !== '???' && } + {!!has_secondary_objectives && name !== '???' && ( + + )} } > @@ -275,6 +288,7 @@ const DomainEntry = (props: DomainEntryProps) => { {desc} {!!is_modular && ' (Modular)'} + {!!has_secondary_objectives && ' (Secondary Objective Available)'} diff --git a/tgui/packages/tgui/interfaces/RestockTracker.jsx b/tgui/packages/tgui/interfaces/RestockTracker.jsx new file mode 100644 index 0000000000000..236f486069cb5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RestockTracker.jsx @@ -0,0 +1,86 @@ +import { sortBy } from 'common/collections'; +import { round } from 'common/math'; + +import { useBackend } from '../backend'; +import { ColorBox, ProgressBar, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +export const Restock = (props) => { + return ( + + + + + + ); +}; + +export const RestockTracker = (props) => { + const { data } = useBackend(); + const vending_list = sortBy((vend) => vend.percentage)( + data.vending_list ?? [], + ); + return ( +
+ + + + Vending Name + + + Location + + + Stock % + + + Credits stored + + +
+ {vending_list?.map((vend) => ( + + + {vend.name} + + + {vend.location} + + 75 + ? 'left' + : vend.percentage > 45 + ? 'right' + : 'center' + } + > + + {round(vend.percentage, 0.01)} + + + 50 ? 'good' : 'bad'} + > + 50 ? 'good' : 'bad'} mr={'5%'} /> + {vend.credits} + + + ))} +
+
+ ); +}; diff --git a/tools/UpdatePaths/Scripts/81465_puzzle machinery.txt b/tools/UpdatePaths/Scripts/81465_puzzle machinery.txt new file mode 100644 index 0000000000000..c6514417c60df --- /dev/null +++ b/tools/UpdatePaths/Scripts/81465_puzzle machinery.txt @@ -0,0 +1,2 @@ +/obj/machinery/puzzle_button/@SUBTYPES : /obj/machinery/puzzle/button/@SUBTYPES{@OLD} +/obj/machinery/puzzle_keycardpad/@SUBTYPES : /obj/machinery/puzzle/keycardpad/@SUBTYPES{@OLD} \ No newline at end of file diff --git a/tools/UpdatePaths/Scripts/81839_webs.txt b/tools/UpdatePaths/Scripts/81839_webs.txt new file mode 100644 index 0000000000000..10341bc2321b3 --- /dev/null +++ b/tools/UpdatePaths/Scripts/81839_webs.txt @@ -0,0 +1,3 @@ +/obj/structure/spider/solid : /obj/structure/spider/stickyweb/sealed/tough{@OLD} +/obj/structure/spider/sticky : /obj/structure/spider/stickyweb/very_sticky{@OLD} +/obj/structure/spider/reflector : /obj/structure/spider/stickyweb/sealed/reflector{@OLD}