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.
+