diff --git a/.dockerignore b/.dockerignore
index 2e6259d23d..9c6ac6b4aa 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,6 +1,5 @@
.dockerignore
.editorconfig
-.travis.yml
GPLv3.txt
LICENSE
README.md
@@ -26,5 +25,4 @@ tgstation.dyn.rsc
libmariadb.dll
rust_g.dll
BSQL.dll
-appveyor.yml
Dockerfile
diff --git a/.gitattributes b/.gitattributes
index c447869d3e..b23dfe6932 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -38,5 +38,8 @@
*.dmm text eol=lf merge=dmm
*.dmi binary merge=dmi
+##Force tab indents on dm files
+*.dm whitespace=indent-with-non-tab
+
## Force changelog merging to use union
html/changelog.html text eol=lf merge=union
diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml
new file mode 100644
index 0000000000..76a1c26656
--- /dev/null
+++ b/.github/workflows/ci_suite.yml
@@ -0,0 +1,114 @@
+name: CI Suite
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ run_linters:
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
+ name: Run Linters
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup cache
+ uses: actions/cache@v2
+ with:
+ path: $HOME/SpacemanDMM
+ key: ${{ runner.os }}-spacemandmm
+ - name: Install Tools
+ run: |
+ pip3 install setuptools
+ bash tools/ci/install_build_tools.sh
+ bash tools/ci/install_spaceman_dmm.sh dreamchecker
+ pip3 install -r tools/mapmerge2/requirements.txt
+ - name: Run Linters
+ run: |
+ bash tools/ci/check_filedirs.sh tgstation.dme
+ bash tools/ci/check_changelogs.sh
+ find . -name "*.php" -print0 | xargs -0 -n1 php -l
+ find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py
+ bash tools/ci/build_tgui.sh
+ bash tools/ci/check_grep.sh
+ python3 tools/mapmerge2/dmi.py --test
+ ~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1
+ - name: Annotate Lints
+ uses: yogstation13/DreamAnnotate@v1
+ if: always()
+ with:
+ outputFile: output-annotations.txt
+
+ compile_all_maps:
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
+ name: Compile Maps
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup cache
+ uses: actions/cache@v2
+ with:
+ path: $HOME/BYOND
+ key: ${{ runner.os }}-byond
+ - name: Compile All Maps
+ run: |
+ bash tools/ci/install_byond.sh
+ source $HOME/BYOND/byond/bin/byondsetup
+ python3 tools/ci/template_dm_generator.py
+ bash tools/ci/dm.sh -DCIBUILDING -DCITESTING -DALL_MAPS tgstation.dme
+ run_all_tests:
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
+ name: Integration Tests
+ runs-on: ubuntu-latest
+ services:
+ mysql:
+ image: mysql:latest
+ env:
+ MYSQL_ROOT_PASSWORD: root
+ ports:
+ - 3306
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup cache
+ uses: actions/cache@v2
+ with:
+ path: $HOME/BYOND
+ key: ${{ runner.os }}-byond
+ - name: Setup database
+ run: |
+ sudo systemctl start mysql
+ mysql -u root -proot -e 'CREATE DATABASE tg_ci;'
+ mysql -u root -proot tg_ci < SQL/tgstation_schema.sql
+ mysql -u root -proot -e 'CREATE DATABASE tg_ci_prefixed;'
+ mysql -u root -proot tg_ci_prefixed < SQL/tgstation_schema_prefixed.sql
+ - name: Install rust-g
+ run: |
+ sudo dpkg --add-architecture i386
+ sudo apt update || true
+ sudo apt install libssl1.1:i386
+ bash tools/ci/install_rust_g.sh
+ # - name: Compile and run tests
+ # run: |
+ # bash tools/ci/install_byond.sh
+ # source $HOME/BYOND/byond/bin/byondsetup
+ # bash tools/ci/dm.sh -DCIBUILDING tgstation.dme
+ # bash tools/ci/run_server.sh
+ test_windows:
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
+ name: Windows Build
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Compile
+ run: pwsh tools/ci/build.ps1
+ - name: Create artifact
+ run: |
+ md deploy
+ bash tools/deploy.sh ./deploy
+ - name: Deploy artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: deploy
+ path: deploy
diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml
index 4fd396f133..c8756f0d28 100644
--- a/.github/workflows/compile_changelogs.yml
+++ b/.github/workflows/compile_changelogs.yml
@@ -15,7 +15,7 @@ jobs:
CHANGELOG_ENABLER: ${{ secrets.CHANGELOG_ENABLER }}
run: |
unset SECRET_EXISTS
- if [-n $CHANGELOG_ENABLER]; then SECRET_EXISTS='true' ; fi
+ if [ -n $CHANGELOG_ENABLER ]; then SECRET_EXISTS='true' ; fi
echo ::set-output name=CL_ENABLED::${SECRET_EXISTS}
- name: "Setup python"
if: steps.value_holder.outputs.CL_ENABLED
diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml
new file mode 100644
index 0000000000..7417a382c4
--- /dev/null
+++ b/.github/workflows/docker_publish.yml
@@ -0,0 +1,22 @@
+name: Docker Build
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Build and Publish Docker Image to Registry
+ uses: elgohr/Publish-Docker-Github-Action@master
+ with:
+ name: tgstation/tgstation
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+ dockerfile: Dockerfile
+ tags: "latest"
+ cache: true
diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml
new file mode 100644
index 0000000000..d0d61be073
--- /dev/null
+++ b/.github/workflows/generate_documentation.yml
@@ -0,0 +1,31 @@
+name: Generate documentation
+on:
+ push:
+ branches:
+ - master
+jobs:
+ generate_documentation:
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup cache
+ uses: actions/cache@v2
+ with:
+ path: $HOME/SpacemanDMM
+ key: ${{ runner.os }}-spacemandmm
+ - name: Install SpacemanDMM
+ run: bash tools/ci/install_spaceman_dmm.sh dmdoc
+ - name: Generate documentation
+ run: |
+ ~/dmdoc
+ touch dmdoc/.nojekyll
+ echo codedocs.tgstation13.org > dmdoc/CNAME
+ - name: Deploy
+ uses: JamesIves/github-pages-deploy-action@3.7.1
+ with:
+ BRANCH: gh-pages
+ CLEAN: true
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SINGLE_COMMIT: true
+ FOLDER: dmdoc
diff --git a/.github/workflows/update_tgs_dmapi.yml b/.github/workflows/update_tgs_dmapi.yml
index 6fe53f700c..c7bb5c970c 100644
--- a/.github/workflows/update_tgs_dmapi.yml
+++ b/.github/workflows/update_tgs_dmapi.yml
@@ -26,6 +26,7 @@ jobs:
library-path: 'code/modules/tgs'
- name: Commit and Push
+ continue-on-error: true
run: |
git config user.name tgstation-server
git config user.email tgstation-server@users.noreply.github.com
@@ -35,6 +36,7 @@ jobs:
- name: Create Pull Request
uses: repo-sync/pull-request@v2
+ if: ${{ success() }}
with:
source_branch: "tgs-dmapi-update"
destination_branch: "master"
@@ -42,4 +44,4 @@ jobs:
pr_body: "This pull request updates the TGS DMAPI to the latest version. Please note any breaking or unimplemented changes before merging."
pr_label: "Tools"
pr_allow_empty: false
- github_token: ${{ secrets.GITHUB_TOKEN }}
+ github_token: ${{ secrets.TGS_UPDATER }}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 83ef6fa8ff..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,102 +0,0 @@
-language: generic
-os: linux
-dist: xenial
-
-branches:
- except:
- - ___TGS3TempBranch
- - ___TGSTempBranch
-
-jobs:
- include:
- - name: "Run Linters"
- addons:
- apt:
- packages:
- - python3
- - python3-pip
- - python3-setuptools
- - pcregrep
- - rustc
- - cargo
- cache:
- directories:
- - $HOME/SpacemanDMM
- install:
- - tools/travis/install_build_tools.sh
- - tools/travis/install_spaceman_dmm.sh dreamchecker
- script:
- - tools/travis/check_filedirs.sh tgstation.dme
- - tools/travis/check_changelogs.sh
- - find . -name "*.php" -print0 | xargs -0 -n1 php -l
- - find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py
- - tools/travis/build_tgui.sh
- - tools/travis/check_grep.sh
- - python3 tools/travis/check_line_endings.py
- - ~/dreamchecker
-
- - name: "Compile All Maps"
- addons:
- apt:
- packages:
- - libstdc++6:i386
- cache:
- directories:
- - $HOME/BYOND
- install:
- - tools/travis/install_byond.sh
- - source $HOME/BYOND/byond/bin/byondsetup
- before_script:
- - tools/travis/template_dm_generator.py
- script:
- - tools/travis/dm.sh -DTRAVISBUILDING -DTRAVISTESTING -DALL_MAPS tgstation.dme
-
- - name: "Compile and Run Tests"
- addons:
- mariadb: '10.2'
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - libstdc++6:i386
- - gcc-multilib
- - g++-7
- - g++-7-multilib
- - libmariadb-client-lgpl-dev:i386
- - libmariadbd-dev
- cache:
- directories:
- - $HOME/BYOND
- - $HOME/libmariadb
- install:
- - tools/travis/install_byond.sh
- - source $HOME/BYOND/byond/bin/byondsetup
- - tools/travis/install_libmariadb.sh
- - tools/travis/install_rust_g.sh
- before_script:
- - mysql -u root -e 'CREATE DATABASE tg_travis;'
- - mysql -u root tg_travis < SQL/tgstation_schema.sql
- - mysql -u root -e 'CREATE DATABASE tg_travis_prefixed;'
- - mysql -u root tg_travis_prefixed < SQL/tgstation_schema_prefixed.sql
- - tools/travis/build_bsql.sh
- script:
- - tools/travis/dm.sh -DTRAVISBUILDING tgstation.dme || travis_terminate 1
- - tools/travis/run_server.sh
-
- # - name: "Generate Documentation"
- # # Only run for non-PR commits to the real master branch.
- # if: branch = master AND head_branch IS blank
- # install:
- # - tools/travis/install_spaceman_dmm.sh dmdoc
- # before_script:
- # # Travis checks out a hash, try to get back on a branch.
- # - git checkout $TRAVIS_BRANCH || true
- # script:
- # - ~/dmdoc
- # - touch dmdoc/.nojekyll
- # deploy:
- # provider: pages
- # skip_cleanup: true
- # local_dir: dmdoc
- # token: $DMDOC_GITHUB_TOKEN
- # fqdn: codedocs.tgstation13.org
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index f79100f563..bf0d9d2fb9 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,9 +1,10 @@
{
- "recommendations": [
- "gbasood.byond-dm-language-support",
- "platymuus.dm-langclient",
+ "recommendations": [
+ "gbasood.byond-dm-language-support",
+ "platymuus.dm-langclient",
"EditorConfig.EditorConfig",
"arcanis.vscode-zipfs",
- "dbaeumer.vscode-eslint"
- ]
+ "dbaeumer.vscode-eslint",
+ "kevinkyang.auto-comment-blocks"
+ ]
}
diff --git a/Dockerfile b/Dockerfile
index e8a5f44908..cca7e43a54 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,14 +1,12 @@
-FROM tgstation/byond:513.1508 as base
+FROM tgstation/byond:513.1533 as base
-FROM base as build_base
+FROM base as rust_g
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
git \
ca-certificates
-FROM build_base as rust_g
-
WORKDIR /rust_g
RUN apt-get install -y --no-install-recommends \
@@ -27,36 +25,6 @@ RUN /bin/bash -c "source dependencies.sh \
&& git checkout FETCH_HEAD \
&& ~/.cargo/bin/cargo build --release
-FROM build_base as bsql
-
-WORKDIR /bsql
-
-RUN apt-get install -y --no-install-recommends software-properties-common \
- && add-apt-repository ppa:ubuntu-toolchain-r/test \
- && apt-get update \
- && apt-get install -y --no-install-recommends \
- cmake \
- make \
- g++-7 \
- libmariadb-client-lgpl-dev \
- && git init \
- && git remote add origin https://github.com/tgstation/BSQL
-
-COPY dependencies.sh .
-
-RUN /bin/bash -c "source dependencies.sh \
- && git fetch --depth 1 origin \$BSQL_VERSION" \
- && git checkout FETCH_HEAD
-
-WORKDIR /bsql/artifacts
-
-ENV CC=gcc-7 CXX=g++-7
-
-RUN ln -s /usr/include/mariadb /usr/include/mysql \
- && ln -s /usr/lib/i386-linux-gnu /root/MariaDB \
- && cmake .. \
- && make
-
FROM base as dm_base
WORKDIR /tgstation
@@ -65,26 +33,30 @@ FROM dm_base as build
COPY . .
-RUN DreamMaker -max_errors 0 tgstation.dme && tools/deploy.sh /deploy
+RUN DreamMaker -max_errors 0 tgstation.dme \
+ && tools/deploy.sh /deploy \
+ && rm /deploy/*.dll
FROM dm_base
EXPOSE 1337
RUN apt-get update \
+ && apt-get install -y --no-install-recommends software-properties-common \
+ && add-apt-repository ppa:ubuntu-toolchain-r/test \
+ && apt-get update \
+ && apt-get upgrade -y \
+ && apt-get dist-upgrade -y \
&& apt-get install -y --no-install-recommends \
+ libmariadb2 \
mariadb-client \
libssl1.0.0 \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /root/.byond/bin
COPY --from=rust_g /rust_g/target/release/librust_g.so /root/.byond/bin/rust_g
-COPY --from=bsql /bsql/artifacts/src/BSQL/libBSQL.so ./
COPY --from=build /deploy ./
-#bsql fexists memes
-RUN ln -s /tgstation/libBSQL.so /root/.byond/bin/libBSQL.so
-
VOLUME [ "/tgstation/config", "/tgstation/data" ]
ENTRYPOINT [ "DreamDaemon", "tgstation.dmb", "-port", "1337", "-trusted", "-close", "-verbose" ]
diff --git a/SpacemanDMM.toml b/SpacemanDMM.toml
index b827472254..81aff0d557 100644
--- a/SpacemanDMM.toml
+++ b/SpacemanDMM.toml
@@ -1,2 +1,9 @@
[langserver]
dreamchecker = true
+
+[code_standards]
+disallow_relative_type_definitions = true
+disallow_relative_proc_definitions = true
+
+[dmdoc]
+use_typepath_names = true
diff --git a/TGS3.json b/TGS3.json
index 39b75bd913..228854a166 100644
--- a/TGS3.json
+++ b/TGS3.json
@@ -1,22 +1,9 @@
{
"documentation": "/tg/station server 3 configuration file",
- "changelog": {
- "script": "tools/ss13_genchangelog.py",
- "arguments": "html/changelog.html html/changelogs",
- "pip_dependancies": [
- "PyYaml",
- "beautifulsoup4"
- ]
- },
- "synchronize_paths": [
- "html/changelog.html",
- "html/changelogs/*"
- ],
+ "synchronize_paths": [],
"static_directories": [
"config",
"data"
],
- "dlls": [
- "libmariadb.dll"
- ]
+ "dlls": []
}
diff --git a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm
index 37a89ba2ff..274c814cf5 100644
--- a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm
+++ b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm
@@ -18,7 +18,7 @@
/obj/item/mining_scanner,
/obj/item/flashlight/lantern,
/obj/item/card/id/mining,
-/obj/item/gps/mining,
+/obj/item/gps/mining/off,
/turf/open/floor/plating,
/area/ruin/powered/golem_ship)
"d" = (
@@ -32,7 +32,7 @@
/obj/item/mining_scanner,
/obj/item/flashlight/lantern,
/obj/item/card/id/mining,
-/obj/item/gps/mining,
+/obj/item/gps/mining/off,
/turf/open/floor/plating,
/area/ruin/powered/golem_ship)
"e" = (
@@ -71,8 +71,8 @@
/area/ruin/powered/golem_ship)
"k" = (
/obj/machinery/computer/arcade/battle{
- icon_state = "arcade";
- dir = 4
+ dir = 4;
+ icon_state = "arcade"
},
/turf/open/floor/mineral/titanium/purple,
/area/ruin/powered/golem_ship)
@@ -112,8 +112,8 @@
/area/ruin/powered/golem_ship)
"s" = (
/obj/machinery/computer/arcade/orion_trail{
- icon_state = "arcade";
- dir = 4
+ dir = 4;
+ icon_state = "arcade"
},
/turf/open/floor/mineral/titanium/purple,
/area/ruin/powered/golem_ship)
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm
index eab08815f3..c2bdea157e 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm
@@ -111,6 +111,17 @@
/obj/structure/alien/resin/membrane,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
+"ax" = (
+/obj/structure/alien/weeds,
+/obj/effect/decal/cleanable/blood/gibs,
+/obj/structure/alien/weeds/node,
+/mob/living/simple_animal/hostile/alien/drone,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/ruin/unpowered/xenonest)
+"ay" = (
+/obj/structure/stone_tile/slab/cracked,
+/turf/open/indestructible/boss,
+/area/ruin/unpowered/xenonest)
"aA" = (
/obj/structure/alien/weeds,
/obj/item/flamethrower,
@@ -130,6 +141,17 @@
},
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
+"aD" = (
+/obj/structure/stone_tile/surrounding_tile{
+ dir = 1
+ },
+/obj/structure/stone_tile/surrounding_tile,
+/obj/structure/stone_tile/surrounding_tile/cracked{
+ dir = 4
+ },
+/obj/structure/stone_tile/center,
+/turf/open/chasm/lavaland,
+/area/ruin/unpowered/xenonest)
"aE" = (
/obj/structure/alien/weeds,
/obj/effect/decal/cleanable/ash,
@@ -160,6 +182,19 @@
/obj/effect/decal/cleanable/blood,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
+"aK" = (
+/obj/structure/stone_tile/surrounding_tile/cracked{
+ dir = 4
+ },
+/obj/structure/stone_tile/surrounding_tile/cracked{
+ dir = 8
+ },
+/obj/structure/stone_tile/surrounding_tile/cracked{
+ dir = 1
+ },
+/obj/structure/stone_tile/center/cracked,
+/turf/open/chasm/lavaland,
+/area/ruin/unpowered/xenonest)
"aL" = (
/obj/structure/alien/weeds,
/obj/item/tank/internals/plasma,
@@ -170,6 +205,26 @@
/obj/effect/decal/cleanable/blood/xeno,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
+"aN" = (
+/obj/structure/stone_tile/block{
+ dir = 8
+ },
+/obj/structure/stone_tile{
+ dir = 1
+ },
+/obj/structure/stone_tile/cracked,
+/turf/open/indestructible/boss,
+/area/ruin/unpowered/xenonest)
+"aO" = (
+/obj/structure/alien/weeds,
+/obj/effect/mob_spawn/alien/corpse/humanoid/sentinel,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/ruin/unpowered/xenonest)
+"aP" = (
+/obj/structure/alien/weeds,
+/obj/effect/mob_spawn/alien/corpse/humanoid/hunter,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/ruin/unpowered/xenonest)
"aQ" = (
/obj/structure/alien/weeds,
/obj/structure/bed/nest,
@@ -215,13 +270,6 @@
/obj/effect/gibspawner/xeno,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
-"aY" = (
-/obj/structure/alien/weeds,
-/obj/effect/decal/cleanable/blood/gibs,
-/mob/living/simple_animal/hostile/alien/drone,
-/obj/structure/alien/weeds/node,
-/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
-/area/ruin/unpowered/xenonest)
"aZ" = (
/turf/closed/indestructible/riveted/boss,
/area/ruin/unpowered/xenonest)
@@ -338,10 +386,6 @@
/obj/structure/stone_tile,
/turf/open/indestructible/boss,
/area/ruin/unpowered/xenonest)
-"bm" = (
-/obj/structure/stone_tile/slab/cracked,
-/turf/open/indestructible/boss,
-/area/ruin/unpowered/xenonest)
"bn" = (
/obj/structure/stone_tile/block{
dir = 4
@@ -365,12 +409,6 @@
/obj/structure/stone_tile/center,
/turf/open/chasm/lavaland,
/area/ruin/unpowered/xenonest)
-"bp" = (
-/obj/structure/stone_tile/block{
- dir = 4
- },
-/turf/open/chasm/lavaland,
-/area/ruin/unpowered/xenonest)
"bq" = (
/turf/open/chasm/lavaland,
/area/ruin/unpowered/xenonest)
@@ -391,33 +429,6 @@
},
/turf/open/chasm/lavaland,
/area/ruin/unpowered/xenonest)
-"bt" = (
-/obj/structure/stone_tile/surrounding_tile{
- dir = 1
- },
-/obj/structure/stone_tile/surrounding_tile,
-/obj/structure/stone_tile/surrounding_tile/cracked{
- dir = 4
- },
-/obj/structure/stone_tile/center,
-/turf/open/chasm/lavaland,
-/area/ruin/unpowered/xenonest)
-"bu" = (
-/obj/structure/stone_tile/block/cracked{
- dir = 1
- },
-/turf/open/chasm/lavaland,
-/area/ruin/unpowered/xenonest)
-"bv" = (
-/obj/structure/stone_tile/block{
- dir = 8
- },
-/obj/structure/stone_tile{
- dir = 1
- },
-/obj/structure/stone_tile/cracked,
-/turf/open/indestructible/boss,
-/area/ruin/unpowered/xenonest)
"bw" = (
/obj/structure/stone_tile/cracked{
dir = 4
@@ -492,19 +503,6 @@
/obj/structure/alien/weeds,
/turf/template_noop,
/area/ruin/unpowered/xenonest)
-"bE" = (
-/obj/structure/stone_tile/surrounding_tile/cracked{
- dir = 4
- },
-/obj/structure/stone_tile/surrounding_tile/cracked{
- dir = 8
- },
-/obj/structure/stone_tile/surrounding_tile/cracked{
- dir = 1
- },
-/obj/structure/stone_tile/center/cracked,
-/turf/open/chasm/lavaland,
-/area/ruin/unpowered/xenonest)
"dE" = (
/obj/structure/alien/weeds,
/obj/structure/bed/nest,
@@ -514,6 +512,12 @@
/obj/item/clothing/mask/facehugger/impregnated,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
+"gA" = (
+/obj/structure/alien/weeds,
+/obj/structure/bed/nest,
+/obj/effect/mob_spawn/alien/corpse/humanoid/sentinel,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/ruin/unpowered/xenonest)
"iA" = (
/obj/structure/alien/weeds,
/obj/structure/bed/nest,
@@ -522,18 +526,24 @@
/obj/item/clothing/mask/facehugger/impregnated,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
-"kp" = (
-/obj/structure/alien/weeds,
-/obj/item/reagent_containers/syringe/alien,
-/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
-/area/ruin/unpowered/xenonest)
"lG" = (
/obj/structure/alien/weeds,
/obj/effect/mob_spawn/alien/corpse/humanoid/drone,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
-"pE" = (
-/obj/effect/mob_spawn/alien/corpse/humanoid/queen,
+"lM" = (
+/obj/structure/stone_tile/block{
+ dir = 4
+ },
+/turf/open/chasm/lavaland,
+/area/ruin/unpowered/xenonest)
+"nj" = (
+/obj/effect/mob_spawn/alien/corpse/humanoid/praetorian,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/ruin/unpowered/xenonest)
+"tY" = (
+/obj/structure/alien/weeds/node,
+/obj/effect/mob_spawn/alien/corpse/humanoid/hunter,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
"wA" = (
@@ -546,12 +556,23 @@
/mob/living/simple_animal/hostile/alien/sentinel,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
+"zj" = (
+/obj/structure/stone_tile/block/cracked{
+ dir = 1
+ },
+/turf/open/chasm/lavaland,
+/area/ruin/unpowered/xenonest)
"Dd" = (
/obj/structure/alien/weeds,
/obj/effect/decal/cleanable/blood/gibs,
/obj/item/storage/backpack/duffelbag/sec/surgery,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
+"Ha" = (
+/obj/structure/alien/weeds,
+/obj/item/reagent_containers/syringe/alien,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/ruin/unpowered/xenonest)
"JM" = (
/obj/structure/alien/weeds/node,
/obj/effect/mob_spawn/alien/corpse/humanoid/drone,
@@ -1075,7 +1096,7 @@ ac
ac
ac
bi
-aY
+ax
ac
ac
ac
@@ -1123,7 +1144,7 @@ an
an
ac
ac
-am
+gA
ac
ac
aJ
@@ -1278,10 +1299,10 @@ ac
Dd
ag
ak
-ag
+aP
af
ac
-lG
+ag
ac
ac
an
@@ -1357,7 +1378,7 @@ ab
ac
af
ag
-ar
+tY
lG
ag
ag
@@ -1511,7 +1532,7 @@ ac
ah
af
ah
-JM
+ar
Vi
ak
ag
@@ -1618,7 +1639,7 @@ ag
wA
ag
ag
-pE
+nj
ag
at
ag
@@ -1640,7 +1661,7 @@ ab
aZ
aZ
aZ
-bm
+ay
aZ
aZ
aZ
@@ -1718,7 +1739,7 @@ ah
ar
ah
ak
-kp
+Ha
at
ag
ag
@@ -1742,8 +1763,8 @@ ac
aZ
bc
bj
-bp
-bt
+lM
+aD
bx
aZ
ab
@@ -1794,7 +1815,7 @@ ba
bd
bk
bq
-bu
+zj
by
aZ
ab
@@ -1823,7 +1844,7 @@ ac
ag
ar
ag
-ag
+aO
ac
ab
ab
@@ -1845,7 +1866,7 @@ aZ
be
bo
bs
-bE
+aK
bz
aZ
ab
@@ -1896,7 +1917,7 @@ aZ
bf
bl
br
-bv
+aN
bA
aZ
ab
diff --git a/_maps/RandomRuins/SpaceRuins/skelter.dmm b/_maps/RandomRuins/SpaceRuins/skelter.dmm
index f6e56a6348..77837392e4 100644
--- a/_maps/RandomRuins/SpaceRuins/skelter.dmm
+++ b/_maps/RandomRuins/SpaceRuins/skelter.dmm
@@ -2729,9 +2729,7 @@
/turf/open/floor/carpet,
/area/ruin/space/has_grav/skelter/admin)
"gx" = (
-/obj/machinery/door/firedoor{
- pixel_x = 0
- },
+/obj/machinery/door/firedoor,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/cable/yellow{
icon_state = "1-2"
@@ -2742,9 +2740,7 @@
/turf/open/floor/plasteel/showroomfloor,
/area/ruin/space/has_grav/skelter/shields)
"gy" = (
-/obj/machinery/door/firedoor{
- pixel_x = 0
- },
+/obj/machinery/door/firedoor,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/door/airlock/engineering{
name = "Shields"
diff --git a/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm b/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm
index 1aed2fdbb0..69cf7c0867 100644
--- a/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm
+++ b/_maps/RandomRuins/StationRuins/Lavaland/Mining_Station/Mining_Station_Public_01.dmm
@@ -3170,7 +3170,9 @@
/turf/open/lava/smooth/lava_land_surface,
/area/lavaland/surface/outdoors)
"VY" = (
-/obj/structure/closet/emcloset,
+/obj/structure/closet/emcloset{
+ anchored = 1
+ },
/obj/effect/turf_decal/tile/purple{
dir = 1
},
diff --git a/_maps/_basemap.dm b/_maps/_basemap.dm
index 213211fc42..bf5b4f7d49 100644
--- a/_maps/_basemap.dm
+++ b/_maps/_basemap.dm
@@ -13,7 +13,7 @@
#include "map_files\BoxStation\BoxStation.dmm"
#include "map_files\LambdaStation\lambda.dmm"
- #ifdef TRAVISBUILDING
+ #ifdef CIBUILDING
#include "templates.dm"
#endif
#endif
diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index b81f55be21..612d4b757c 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -12995,22 +12995,35 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
-/turf/open/floor/plasteel/dark,
+/obj/machinery/hydroponics/soil,
+/turf/open/floor/grass,
/area/chapel/main)
"aCP" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 10
},
-/turf/open/floor/plasteel/dark,
+/obj/structure/table/wood,
+/obj/item/reagent_containers/food/snacks/grown/poppy{
+ pixel_y = 5
+ },
+/obj/item/reagent_containers/food/snacks/grown/harebell{
+ pixel_y = 5
+ },
+/obj/machinery/light{
+ dir = 1
+ },
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aCQ" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
-/obj/machinery/light/small{
- dir = 1
+/obj/machinery/hydroponics/soil,
+/obj/structure/window/reinforced{
+ dir = 4;
+ pixel_x = 3
},
-/turf/open/floor/plasteel/dark,
+/turf/open/floor/grass,
/area/chapel/main)
"aCR" = (
/turf/closed/wall,
@@ -13536,7 +13549,7 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
},
-/turf/open/floor/plasteel/dark,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aEi" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
@@ -13556,35 +13569,24 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 10
},
-/turf/open/floor/plasteel/dark,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aEl" = (
/obj/effect/landmark/event_spawn,
/turf/open/floor/plating,
/area/maintenance/starboard/fore)
"aEm" = (
-/obj/machinery/door/window{
- dir = 8;
- name = "Mass Driver";
- req_access_txt = "22"
+/obj/structure/bookcase,
+/obj/machinery/camera{
+ c_tag = "Chapel North"
},
-/obj/machinery/mass_driver{
- dir = 4;
- id = "chapelgun";
- name = "Holy Driver"
- },
-/obj/effect/turf_decal/stripes/line{
- dir = 8
- },
-/turf/open/floor/plating,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aEn" = (
-/obj/machinery/door/poddoor{
- id = "chapelgun";
- name = "Chapel Launcher Door"
+/obj/structure/sign/warning/vacuum/external{
+ pixel_y = 32
},
-/obj/structure/fans/tiny,
-/turf/open/floor/plating,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aEz" = (
/obj/machinery/power/apc{
@@ -13978,16 +13980,10 @@
/turf/open/floor/plasteel/dark,
/area/chapel/main)
"aFA" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/machinery/computer/pod/old{
- density = 0;
- icon = 'icons/obj/airlock_machines.dmi';
- icon_state = "airlock_control_standby";
- id = "chapelgun";
- name = "Mass Driver Controller";
- pixel_x = 24
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 8
},
-/turf/open/floor/plasteel/dark,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aFB" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
@@ -14617,7 +14613,7 @@
departmentType = 2;
pixel_y = 30
},
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aGV" = (
/obj/structure/disposalpipe/segment{
@@ -14652,7 +14648,7 @@
/obj/machinery/airalarm{
pixel_y = 25
},
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aGZ" = (
/obj/machinery/door/airlock/security{
@@ -14703,7 +14699,7 @@
pixel_y = 25
},
/obj/machinery/vending/wardrobe/chap_wardrobe,
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aHg" = (
/obj/machinery/light_switch{
@@ -14712,7 +14708,7 @@
/obj/machinery/camera{
c_tag = "Chapel Office"
},
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aHh" = (
/obj/structure/cable{
@@ -14724,9 +14720,9 @@
/turf/open/floor/plasteel,
/area/gateway)
"aHi" = (
-/obj/structure/closet/crate/coffin,
-/obj/structure/window/reinforced{
- dir = 8
+/obj/machinery/door/morgue{
+ name = "Confession Booth (Chaplain)";
+ req_access_txt = "22"
},
/turf/open/floor/plasteel/dark,
/area/chapel/office)
@@ -14743,42 +14739,47 @@
/turf/open/floor/plasteel,
/area/gateway)
"aHk" = (
-/obj/machinery/atmospherics/components/unary/vent_pump/on{
- dir = 4
- },
-/turf/open/floor/plasteel/dark,
+/obj/effect/decal/cleanable/cobweb/cobweb2,
+/obj/structure/table/wood,
+/obj/item/clothing/under/misc/burial,
+/obj/item/clothing/under/misc/burial,
+/obj/item/clothing/under/misc/burial,
+/obj/item/clothing/under/misc/burial,
+/obj/item/clothing/under/misc/burial,
+/obj/item/clothing/under/misc/burial,
+/obj/item/clothing/under/misc/burial,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aHl" = (
-/obj/structure/closet/crate/coffin,
-/obj/machinery/door/window/eastleft{
- name = "Coffin Storage";
- req_access_txt = "22"
+/obj/structure/chair/comfy/plywood,
+/obj/machinery/light/floor,
+/obj/item/radio/intercom{
+ broadcasting = 1;
+ frequency = 1480;
+ name = "Confessional Intercom";
+ pixel_x = 25
},
+/obj/effect/decal/cleanable/cobweb/cobweb2,
/turf/open/floor/plasteel/dark,
/area/chapel/office)
"aHm" = (
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
- dir = 4
- },
-/turf/open/floor/plasteel/dark,
+/obj/structure/lattice,
+/turf/closed/wall,
/area/chapel/main)
"aHn" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/obj/machinery/atmospherics/pipe/simple/supply/hidden{
- dir = 4
+/obj/machinery/mass_driver{
+ dir = 4;
+ id = "chapelgun";
+ name = "Holy Driver"
},
-/turf/open/floor/plasteel/dark,
-/area/chapel/main)
-"aHo" = (
-/obj/structure/table/glass,
-/obj/item/reagent_containers/food/snacks/grown/poppy,
-/obj/item/reagent_containers/food/snacks/grown/harebell,
-/turf/open/floor/plasteel/chapel{
- dir = 4
+/obj/machinery/door/window{
+ dir = 8;
+ name = "Mass Driver";
+ req_access_txt = "22"
+ },
+/obj/effect/turf_decal/stripes/line{
+ dir = 8
},
-/area/chapel/main)
-"aHq" = (
-/obj/effect/spawner/structure/window/reinforced,
/turf/open/floor/plating,
/area/chapel/main)
"aHu" = (
@@ -15278,9 +15279,14 @@
/turf/open/floor/plasteel/dark,
/area/chapel/office)
"aIz" = (
-/obj/machinery/disposal/bin,
-/obj/structure/disposalpipe/trunk,
-/turf/open/floor/plasteel/grimy,
+/obj/structure/disposalpipe/segment{
+ dir = 6
+ },
+/obj/structure/table/wood,
+/obj/item/storage/crayons{
+ pixel_y = 8
+ },
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aIB" = (
/obj/structure/bodycontainer/crematorium{
@@ -15293,20 +15299,16 @@
/area/chapel/office)
"aIC" = (
/obj/effect/landmark/start/chaplain,
-/obj/structure/chair,
-/turf/open/floor/plasteel/grimy,
-/area/chapel/office)
-"aID" = (
-/obj/structure/closet/crate/coffin,
-/obj/structure/window/reinforced{
+/obj/structure/chair/comfy/plywood,
+/obj/structure/disposalpipe/segment{
dir = 4
},
+/turf/open/floor/carpet,
+/area/chapel/office)
+"aID" = (
+/obj/effect/spawner/structure/window/reinforced/tinted,
/turf/open/floor/plasteel/dark,
/area/chapel/office)
-"aIE" = (
-/obj/structure/table/glass,
-/turf/open/floor/plasteel/chapel,
-/area/chapel/main)
"aIH" = (
/obj/structure/table,
/obj/item/storage/box/lights/mixed,
@@ -15328,7 +15330,7 @@
/area/construction/mining/aux_base)
"aII" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on,
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aIJ" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
@@ -15783,15 +15785,14 @@
/area/hydroponics)
"aJM" = (
/obj/structure/table/wood,
-/obj/item/flashlight/lamp{
- pixel_y = 10
- },
/obj/structure/disposalpipe/segment,
-/obj/item/nullrod,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
},
-/turf/open/floor/plasteel/grimy,
+/obj/item/flashlight/lamp{
+ pixel_y = 15
+ },
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aJO" = (
/obj/structure/table,
@@ -15825,39 +15826,44 @@
/area/library)
"aJT" = (
/obj/structure/table/wood,
-/obj/item/paper_bin{
- pixel_x = -2;
- pixel_y = 5
- },
-/obj/item/storage/crayons,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
},
-/turf/open/floor/plasteel/grimy,
+/obj/item/paper_bin{
+ pixel_y = 4
+ },
+/obj/item/pen/fountain{
+ pixel_y = 4
+ },
+/turf/open/floor/carpet,
/area/chapel/office)
"aJU" = (
/obj/structure/table/wood,
-/obj/item/pen,
-/obj/item/reagent_containers/food/drinks/bottle/holywater,
+/obj/item/reagent_containers/food/drinks/bottle/holywater{
+ pixel_x = 9;
+ pixel_y = 4
+ },
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
},
-/turf/open/floor/plasteel/grimy,
+/obj/item/nullrod{
+ pixel_x = -15;
+ pixel_y = 3
+ },
+/turf/open/floor/carpet,
/area/chapel/office)
"aJV" = (
-/obj/structure/closet/crate/coffin,
-/obj/machinery/door/window/eastleft{
- dir = 8;
- name = "Coffin Storage";
- req_access_txt = "22"
+/obj/effect/landmark/event_spawn,
+/obj/effect/turf_decal/stripes/line{
+ dir = 4
},
-/turf/open/floor/plasteel/dark,
-/area/chapel/office)
+/turf/open/floor/plating,
+/area/chapel/main)
"aJW" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 9
},
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aJX" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
@@ -15916,10 +15922,10 @@
/turf/open/floor/plasteel,
/area/gateway)
"aKe" = (
-/obj/structure/table/glass,
-/turf/open/floor/plasteel/chapel{
+/obj/structure/chair/wood/normal{
dir = 4
},
+/turf/open/floor/carpet,
/area/chapel/main)
"aKf" = (
/obj/structure/cable{
@@ -16248,7 +16254,7 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aLb" = (
/obj/structure/disposalpipe/segment{
@@ -16267,7 +16273,7 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on{
dir = 8
},
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aLd" = (
/obj/structure/table,
@@ -16286,13 +16292,13 @@
/turf/open/floor/plasteel,
/area/hydroponics)
"aLe" = (
-/obj/structure/chair{
- dir = 1
- },
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
-/turf/open/floor/plasteel/grimy,
+/obj/structure/chair/wood/normal{
+ dir = 1
+ },
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aLf" = (
/obj/machinery/airalarm{
@@ -16816,13 +16822,14 @@
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
dir = 4
},
+/obj/structure/chair/wood/wings{
+ dir = 8
+ },
/turf/open/floor/plasteel/dark,
/area/chapel/main)
"aMM" = (
-/obj/machinery/camera{
- c_tag = "Chapel North"
- },
-/turf/open/floor/plasteel/dark,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aMN" = (
/obj/machinery/chem_master/condimaster,
@@ -16887,7 +16894,7 @@
/turf/open/floor/plasteel,
/area/hallway/primary/port)
"aMX" = (
-/turf/open/floor/plasteel/grimy,
+/turf/open/floor/wood/wood_large,
/area/chapel/office)
"aMY" = (
/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden,
@@ -17177,28 +17184,24 @@
/turf/open/floor/wood,
/area/library)
"aNW" = (
-/obj/machinery/door/airlock/public/glass{
- name = "Chapel Office";
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
+"aNX" = (
+/obj/structure/closet/crate/coffin,
+/obj/machinery/door/window/eastright{
+ dir = 8;
+ name = "Coffin Storage";
req_access_txt = "22"
},
/turf/open/floor/plasteel/dark,
-/area/chapel/office)
-"aNX" = (
-/obj/item/radio/intercom{
- broadcasting = 1;
- frequency = 1480;
- name = "Confessional Intercom";
- pixel_x = 25
- },
-/obj/structure/chair,
-/turf/open/floor/plasteel/dark,
/area/chapel/main)
"aNY" = (
-/obj/machinery/door/morgue{
- name = "Confession Booth (Chaplain)";
- req_access_txt = "22"
+/obj/machinery/door/poddoor{
+ id = "chapelgun";
+ name = "Chapel Launcher Door"
},
-/turf/open/floor/plasteel/dark,
+/obj/structure/fans/tiny,
+/turf/open/floor/plating,
/area/chapel/main)
"aNZ" = (
/obj/structure/chair,
@@ -17679,7 +17682,12 @@
},
/area/chapel/main)
"aPo" = (
-/obj/effect/spawner/structure/window/reinforced/tinted,
+/obj/structure/closet/crate/coffin,
+/obj/machinery/door/window/eastleft{
+ dir = 8;
+ name = "Coffin Storage";
+ req_access_txt = "22"
+ },
/turf/open/floor/plasteel/dark,
/area/chapel/main)
"aPp" = (
@@ -18085,7 +18093,11 @@
/area/chapel/main)
"aQw" = (
/obj/structure/table/wood,
-/turf/open/floor/plasteel/dark,
+/obj/item/trash/candle{
+ pixel_x = -5;
+ pixel_y = 2
+ },
+/turf/open/floor/carpet,
/area/chapel/main)
"aQx" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
@@ -18098,22 +18110,17 @@
},
/area/chapel/main)
"aQz" = (
-/obj/item/radio/intercom{
- broadcasting = 1;
- frequency = 1480;
- name = "Confessional Intercom";
- pixel_x = 25
- },
-/obj/structure/chair{
- dir = 1
+/obj/structure/closet/crate/coffin,
+/obj/structure/window/reinforced{
+ dir = 8
},
/turf/open/floor/plasteel/dark,
/area/chapel/main)
"aQA" = (
-/obj/machinery/door/morgue{
- name = "Confession Booth"
+/obj/machinery/atmospherics/components/unary/vent_pump/on{
+ dir = 8
},
-/turf/open/floor/plasteel/dark,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"aQB" = (
/obj/effect/turf_decal/tile/red{
@@ -19111,24 +19118,32 @@
/turf/open/floor/plasteel/dark,
/area/chapel/main)
"aTf" = (
-/obj/structure/chair/stool,
+/obj/structure/chair/pew/right{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel,
/area/chapel/main)
"aTg" = (
-/obj/structure/chair/stool,
+/obj/structure/chair/pew/left{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel{
dir = 8
},
/area/chapel/main)
"aTh" = (
-/obj/structure/chair/stool,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/structure/chair/pew/right{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel,
/area/chapel/main)
"aTi" = (
-/obj/structure/chair/stool,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/effect/landmark/start/assistant,
+/obj/structure/chair/pew/left{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel{
dir = 8
},
@@ -19659,28 +19674,36 @@
/turf/open/floor/plasteel/dark,
/area/chapel/main)
"aUH" = (
-/obj/structure/chair/stool,
/obj/effect/landmark/start/assistant,
+/obj/structure/chair/pew/right{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel{
dir = 4
},
/area/chapel/main)
"aUI" = (
-/obj/structure/chair/stool,
+/obj/structure/chair/pew/left{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel{
dir = 1
},
/area/chapel/main)
"aUJ" = (
-/obj/structure/chair/stool,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/structure/chair/pew/right{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel{
dir = 4
},
/area/chapel/main)
"aUK" = (
-/obj/structure/chair/stool,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/structure/chair/pew/left{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel{
dir = 1
},
@@ -20207,8 +20230,10 @@
/turf/open/floor/wood,
/area/library)
"aVU" = (
-/obj/structure/chair/stool,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/structure/chair/pew/left{
+ dir = 1
+ },
/turf/open/floor/plasteel/chapel{
dir = 8
},
@@ -21999,13 +22024,6 @@
},
/turf/open/floor/wood,
/area/crew_quarters/heads/captain)
-"bag" = (
-/obj/machinery/airalarm{
- dir = 1;
- pixel_y = -22
- },
-/turf/open/floor/wood,
-/area/crew_quarters/bar)
"bah" = (
/obj/structure/extinguisher_cabinet{
pixel_y = -30
@@ -23874,14 +23892,15 @@
/turf/closed/wall,
/area/maintenance/disposal)
"bfb" = (
-/obj/structure/sign/warning/vacuum/external{
- pixel_y = 32
+/obj/machinery/computer/pod/old{
+ density = 0;
+ icon = 'icons/obj/airlock_machines.dmi';
+ icon_state = "airlock_control_standby";
+ id = "chapelgun";
+ name = "Mass Driver Controller";
+ pixel_x = 24
},
-/obj/effect/landmark/event_spawn,
-/obj/effect/turf_decal/stripes/line{
- dir = 4
- },
-/turf/open/floor/plating,
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"bfc" = (
/obj/machinery/power/apc{
@@ -34004,9 +34023,6 @@
/area/medical/surgery)
"bCJ" = (
/obj/machinery/atmospherics/pipe/manifold/supply/hidden,
-/obj/machinery/light_switch{
- pixel_y = 26
- },
/turf/open/floor/plasteel/white,
/area/medical/surgery)
"bCK" = (
@@ -46267,7 +46283,7 @@
"chY" = (
/obj/machinery/shieldgen,
/turf/open/floor/plating,
-/area/engine/storage)
+/area/engine/engineering)
"cia" = (
/obj/effect/turf_decal/bot{
dir = 1
@@ -46617,7 +46633,7 @@
"ciW" = (
/obj/effect/landmark/blobstart,
/turf/open/floor/plating,
-/area/engine/storage)
+/area/engine/engineering)
"ciX" = (
/obj/structure/closet/crate,
/obj/item/stack/sheet/metal/fifty,
@@ -46632,14 +46648,14 @@
/obj/item/lightreplacer,
/obj/item/lightreplacer,
/turf/open/floor/plating,
-/area/engine/storage)
+/area/engine/engineering)
"ciY" = (
/obj/machinery/door/poddoor{
id = "Secure Storage";
name = "secure storage"
},
/turf/open/floor/plating,
-/area/engine/storage)
+/area/engine/engineering)
"ciZ" = (
/turf/open/floor/plating,
/area/engine/engineering)
@@ -46947,7 +46963,7 @@
dir = 4
},
/turf/open/floor/plating,
-/area/engine/storage)
+/area/engine/engineering)
"cjN" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
@@ -47234,11 +47250,11 @@
"ckB" = (
/obj/machinery/field/generator,
/turf/open/floor/plating,
-/area/engine/storage)
+/area/engine/engineering)
"ckC" = (
/obj/machinery/power/emitter,
/turf/open/floor/plating,
-/area/engine/storage)
+/area/engine/engineering)
"ckD" = (
/obj/effect/turf_decal/bot{
dir = 1
@@ -49323,9 +49339,12 @@
/turf/open/floor/plating,
/area/ai_monitored/turret_protected/aisat_interior)
"csT" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/obj/effect/landmark/xmastree,
-/turf/open/floor/plasteel/dark,
+/obj/structure/table/glass,
+/obj/effect/decal/cleanable/cobweb/cobweb2,
+/obj/item/storage/book/bible{
+ pixel_y = -1
+ },
+/turf/open/floor/wood/wood_large,
/area/chapel/main)
"csU" = (
/obj/structure/transit_tube/station/reverse,
@@ -51957,14 +51976,11 @@
/turf/open/floor/plating,
/area/maintenance/starboard/aft)
"cBZ" = (
-/obj/structure/table/wood,
-/obj/item/clothing/under/misc/burial,
-/obj/item/clothing/under/misc/burial,
-/obj/item/clothing/under/misc/burial,
-/obj/item/clothing/under/misc/burial,
-/obj/item/clothing/under/misc/burial,
-/obj/item/clothing/under/misc/burial,
-/turf/open/floor/plasteel/grimy,
+/obj/structure/disposalpipe/trunk{
+ dir = 8
+ },
+/obj/machinery/disposal/bin,
+/turf/open/floor/carpet,
/area/chapel/office)
"cCb" = (
/obj/structure/table,
@@ -53407,10 +53423,6 @@
dir = 4
},
/obj/machinery/suit_storage_unit/atmos,
-/obj/machinery/airalarm{
- dir = 1;
- pixel_y = -22
- },
/obj/effect/turf_decal/stripes/line{
dir = 9
},
@@ -53432,6 +53444,10 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plasteel/dark,
/area/hallway/primary/central)
+"dmX" = (
+/obj/effect/spawner/structure/window/reinforced,
+/turf/open/floor/plating,
+/area/chapel/main)
"dnW" = (
/obj/effect/turf_decal/trimline/blue/filled/line{
dir = 8
@@ -53462,6 +53478,14 @@
icon_state = "carpetsymbol"
},
/area/crew_quarters/theatre)
+"dsJ" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/structure/table/glass,
+/obj/item/toy/figure/chaplain{
+ pixel_y = -9
+ },
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"dtx" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -54381,8 +54405,9 @@
/turf/open/floor/plasteel,
/area/security/range)
"fsQ" = (
-/turf/open/floor/plating,
-/area/engine/storage)
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"fty" = (
/obj/structure/lattice,
/obj/machinery/atmospherics/pipe/simple/orange/visible,
@@ -56336,6 +56361,17 @@
/obj/machinery/atmospherics/pipe/simple/dark/visible,
/turf/open/space/basic,
/area/space/nearstation)
+"kdF" = (
+/obj/structure/window/reinforced{
+ dir = 1
+ },
+/obj/structure/closet/crate/coffin,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/effect/decal/cleanable/cobweb/cobweb2,
+/turf/open/floor/plasteel/dark,
+/area/chapel/main)
"kdO" = (
/obj/machinery/pool/controller,
/turf/open/floor/plasteel/yellowsiding,
@@ -56734,6 +56770,10 @@
},
/turf/open/floor/plating,
/area/maintenance/port/aft)
+"kOL" = (
+/obj/structure/table/glass,
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"kPd" = (
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/obj/structure/cable{
@@ -57074,6 +57114,15 @@
},
/turf/open/floor/plasteel,
/area/science/circuit)
+"lNB" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/effect/landmark/xmastree{
+ pixel_x = 14
+ },
+/turf/open/floor/carpet,
+/area/chapel/main)
"lNH" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/cable{
@@ -57332,6 +57381,10 @@
/obj/effect/spawner/lootdrop/keg,
/turf/open/floor/wood,
/area/maintenance/bar)
+"mtU" = (
+/obj/structure/sign/departments/holy,
+/turf/closed/wall,
+/area/chapel/main)
"mug" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
dir = 8
@@ -57366,6 +57419,12 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/wood,
/area/crew_quarters/theatre)
+"mzv" = (
+/obj/machinery/door/morgue{
+ name = "Confession Booth"
+ },
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"mzB" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/machinery/door/window,
@@ -57575,6 +57634,14 @@
},
/turf/open/floor/plasteel,
/area/crew_quarters/locker)
+"mZx" = (
+/obj/structure/table/glass,
+/obj/item/storage/box/matches{
+ pixel_x = 4;
+ pixel_y = -8
+ },
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"naI" = (
/turf/open/space,
/area/space/station_ruins)
@@ -58844,6 +58911,14 @@
icon_state = "wood-broken2"
},
/area/maintenance/port/fore)
+"pXG" = (
+/obj/structure/table/wood,
+/obj/item/candle{
+ pixel_x = 5;
+ pixel_y = 2
+ },
+/turf/open/floor/carpet,
+/area/chapel/main)
"pYQ" = (
/obj/structure/reagent_dispensers/watertank,
/obj/item/reagent_containers/glass/bucket,
@@ -58938,6 +59013,13 @@
},
/turf/open/floor/plating,
/area/maintenance/port/fore)
+"qkn" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/machinery/light{
+ dir = 8
+ },
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"qkC" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -59027,6 +59109,11 @@
/obj/structure/lattice,
/turf/closed/wall/r_wall,
/area/crew_quarters/heads/captain)
+"qCR" = (
+/obj/structure/musician/piano,
+/obj/effect/decal/cleanable/cobweb,
+/turf/open/floor/plasteel/dark,
+/area/chapel/main)
"qEB" = (
/obj/machinery/hydroponics/soil{
pixel_y = 8
@@ -59160,6 +59247,13 @@
},
/turf/open/floor/plasteel/white,
/area/medical/medbay/lobby)
+"qUh" = (
+/obj/machinery/door/airlock/public/glass{
+ name = "Chapel Office";
+ req_access_txt = "22"
+ },
+/turf/open/floor/plasteel/dark,
+/area/chapel/office)
"qVP" = (
/obj/effect/spawner/structure/window,
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
@@ -59442,6 +59536,14 @@
},
/turf/open/floor/plasteel,
/area/security/prison)
+"rxF" = (
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/structure/table/glass,
+/obj/item/storage/book/bible{
+ pixel_y = 17
+ },
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"ryr" = (
/obj/effect/turf_decal/tile/blue,
/obj/effect/turf_decal/tile/blue{
@@ -60469,6 +60571,12 @@
},
/turf/open/floor/wood,
/area/maintenance/bar)
+"tSm" = (
+/obj/item/kirbyplants{
+ icon_state = "plant-18"
+ },
+/turf/open/floor/plasteel/dark,
+/area/chapel/main)
"tSo" = (
/obj/structure/lattice,
/obj/machinery/atmospherics/pipe/simple/dark/visible{
@@ -61202,6 +61310,19 @@
/obj/item/clothing/under/misc/pj/blue,
/turf/open/floor/plasteel,
/area/crew_quarters/fitness)
+"vqE" = (
+/obj/structure/chair/wood/normal{
+ dir = 1
+ },
+/obj/machinery/light/floor,
+/obj/item/radio/intercom{
+ broadcasting = 1;
+ frequency = 1480;
+ name = "Confessional Intercom";
+ pixel_x = -25
+ },
+/turf/open/floor/plasteel/dark,
+/area/chapel/main)
"vqP" = (
/obj/structure/bed/dogbed{
desc = "A comfy-looking pet bed. You can even strap your pet in, in case the gravity turns off.";
@@ -62236,8 +62357,17 @@
/turf/open/floor/carpet,
/area/library)
"xES" = (
-/turf/closed/wall/r_wall,
-/area/engine/storage)
+/obj/structure/table/glass,
+/obj/item/storage/fancy/candle_box{
+ pixel_x = 5;
+ pixel_y = 4
+ },
+/obj/item/storage/fancy/candle_box{
+ pixel_x = 1;
+ pixel_y = 4
+ },
+/turf/open/floor/wood/wood_large,
+/area/chapel/main)
"xFM" = (
/obj/item/clothing/gloves/color/rainbow,
/obj/item/clothing/head/soft/rainbow,
@@ -62327,6 +62457,12 @@
/obj/item/instrument/trombone,
/turf/open/floor/wood,
/area/crew_quarters/theatre)
+"xRa" = (
+/obj/item/kirbyplants{
+ icon_state = "plant-20"
+ },
+/turf/open/floor/plasteel/dark,
+/area/chapel/main)
"xSW" = (
/obj/effect/turf_decal/tile/red{
dir = 1
@@ -87889,14 +88025,14 @@ cdc
cdZ
bVI
cay
-xES
-xES
-xES
-xES
-xES
-xES
-xES
-xES
+ccw
+ccw
+ccw
+ccw
+ccw
+ccw
+ccw
+ccw
cfL
coH
cBO
@@ -88146,14 +88282,14 @@ bWB
cec
bVI
kNv
-xES
+ccw
chY
ciX
cjM
ckB
ckB
ckB
-xES
+ccw
cnY
coH
cgR
@@ -88403,14 +88539,14 @@ cde
ceb
bVI
cay
-xES
+ccw
chY
-fsQ
+ciZ
ciW
ckB
ckB
ckC
-xES
+ccw
cnX
coH
cps
@@ -88660,14 +88796,14 @@ cdf
ced
bVI
cay
-xES
-fsQ
-fsQ
-fsQ
+ccw
+ciZ
+ciZ
+ciZ
ckC
ckC
ckC
-xES
+ccw
coa
coJ
clJ
@@ -88917,14 +89053,14 @@ bWB
bWB
bVI
cay
-xES
-xES
+ccw
+ccw
ciY
ciY
-xES
-xES
-xES
-xES
+ccw
+ccw
+ccw
+ccw
cnZ
coH
cgI
@@ -99147,7 +99283,7 @@ aSZ
aQc
qaY
acN
-bag
+aKR
aJC
aYV
aYV
@@ -108390,7 +108526,7 @@ cBZ
aJT
aLc
aFw
-aFz
+tSm
aFz
aFz
aRR
@@ -108400,7 +108536,7 @@ aFz
aRS
aXW
baz
-aCR
+mtU
bcx
aXq
aYV
@@ -108646,7 +108782,7 @@ aGY
aII
aJW
aMX
-aNW
+qUh
aFz
aPl
aQv
@@ -108656,7 +108792,7 @@ aUI
aTg
aRS
aZf
-aFz
+xRa
bbF
aYV
aXq
@@ -108900,8 +109036,8 @@ aCM
aEg
aFw
aHi
-aHi
-aJV
+aFw
+aFw
aFw
aFw
aFz
@@ -109158,18 +109294,18 @@ aEi
aFw
aHl
aID
-aID
-aFw
-aMM
-aFz
+vqE
+aCR
+qCR
aFz
+aRS
aQw
aRS
aRS
aRS
aRS
aRS
-aZf
+lNB
aRS
bbF
aYV
@@ -109412,15 +109548,15 @@ aAz
asB
aCO
aEh
-aFz
-aHk
-aFz
-aFz
-aTe
+aCR
+aCR
+aCR
+mzv
+aCR
aML
aFz
-aFz
-aQw
+aRS
+pXG
cdl
aRS
aRS
@@ -109669,11 +109805,11 @@ aAA
asB
aCQ
aEk
-aFB
-aHn
-aFB
-csT
-aFB
+fsQ
+dsJ
+rxF
+fsQ
+qkn
aLr
aFB
aPn
@@ -109925,12 +110061,12 @@ awO
awO
asB
aCP
-aEj
+aMM
aFA
-aHm
-aEj
-aEj
-aEj
+aMM
+aMM
+aMM
+aMM
aEj
aEj
aPm
@@ -109941,7 +110077,7 @@ aUJ
aTh
aXz
aZg
-aFz
+xRa
bbF
aYV
bdv
@@ -110181,18 +110317,18 @@ ayj
azx
aAB
asB
-aCR
aEm
-aCR
-aPl
-aQv
-aPl
-aQv
-aCR
-aNY
-aCR
+aNW
aQA
-aCR
+aNW
+aNW
+aNW
+aNW
+aFz
+aFz
+aFz
+aFz
+aFz
aTj
aFz
aVV
@@ -110438,14 +110574,14 @@ asB
asB
asB
asB
-aCR
+aHk
+aNW
bfb
-aCR
-aHo
-aIE
aKe
-aIE
-aCR
+aKe
+aKe
+aNW
+kdF
aNX
aPo
aQz
@@ -110694,14 +110830,14 @@ atS
aaf
aaf
aaf
-atS
+asB
aCR
aEn
aCR
-aHq
-aHq
-aHq
-aHq
+aKe
+aKe
+aKe
+aNW
aCR
aCR
aCR
@@ -110951,14 +111087,14 @@ atS
aoV
aoV
aaf
-atS
-aaf
-aaa
-aaf
-aaa
-aaa
-aaa
-aaa
+gXs
+aHm
+aHn
+dmX
+aNW
+aNW
+aNW
+aNW
aMZ
aNZ
aPp
@@ -111208,14 +111344,14 @@ aaH
aoV
aoV
aaf
-atS
-aaf
-aaa
-aaf
-aaa
-aaa
-aaa
-aaf
+gXs
+aHm
+aJV
+dmX
+csT
+kOL
+mZx
+xES
aMZ
aOb
aPr
@@ -111465,14 +111601,14 @@ aaH
aoV
aoV
aoV
-atS
-aaf
-aaa
-aaf
-aaa
-aaa
-aaa
-aaf
+gXs
+aHm
+aNY
+aCR
+dmX
+dmX
+dmX
+dmX
aMZ
aOa
aVX
@@ -111722,7 +111858,7 @@ atS
aoV
aoV
aoV
-atS
+gXs
aaf
aaa
aaf
diff --git a/_maps/map_files/CogStation/CogStation.dmm b/_maps/map_files/CogStation/CogStation.dmm
index 6f5bab0498..e41f6c7d89 100644
--- a/_maps/map_files/CogStation/CogStation.dmm
+++ b/_maps/map_files/CogStation/CogStation.dmm
@@ -6004,12 +6004,12 @@
c_tag = "Security Checkpoint";
pixel_x = 22
},
-/obj/machinery/newscaster/security_unit{
- pixel_y = 32
- },
/obj/structure/cable{
icon_state = "4-8"
},
+/obj/machinery/airalarm{
+ pixel_y = 23
+ },
/turf/open/floor/plasteel,
/area/security/checkpoint)
"aoz" = (
@@ -32290,10 +32290,6 @@
dir = 8;
light_color = "#e8eaff"
},
-/obj/machinery/airalarm{
- dir = 4;
- pixel_x = -22
- },
/turf/open/floor/plasteel,
/area/hydroponics)
"bsl" = (
@@ -71184,6 +71180,13 @@
/obj/machinery/atmospherics/pipe/simple/yellow/visible,
/turf/open/floor/plasteel,
/area/engine/atmos)
+"pMW" = (
+/obj/machinery/airalarm{
+ dir = 1;
+ pixel_y = -22
+ },
+/turf/open/floor/plasteel,
+/area/hydroponics)
"pZq" = (
/obj/structure/disposalpipe/sorting/mail/flip{
dir = 8;
@@ -121382,7 +121385,7 @@ amV
aqI
aqB
aGO
-auy
+pMW
aqB
aqI
amV
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 07902b28b1..368b85938c 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -49530,7 +49530,7 @@
},
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/effect/turf_decal/stripes/line{
- dir = 10
+ dir = 8
},
/turf/open/floor/plasteel,
/area/engine/gravity_generator)
@@ -113323,10 +113323,6 @@
/obj/item/clothing/gloves/color/black,
/obj/item/storage/box/evidence,
/obj/item/taperecorder,
-/obj/machinery/airalarm{
- dir = 8;
- pixel_x = 24
- },
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm
index 408efad32f..1b37eea20c 100644
--- a/_maps/map_files/KiloStation/KiloStation.dmm
+++ b/_maps/map_files/KiloStation/KiloStation.dmm
@@ -30529,9 +30529,6 @@
dir = 8
},
/obj/structure/closet/bombcloset,
-/obj/machinery/airalarm{
- pixel_y = 24
- },
/turf/open/floor/plasteel/dark,
/area/science/mixing)
"aXC" = (
@@ -43701,10 +43698,6 @@
/obj/structure/window/reinforced{
dir = 1
},
-/obj/machinery/airalarm{
- dir = 4;
- pixel_x = -22
- },
/mob/living/simple_animal/chicken{
desc = "The arch-nemesis of Kentucky.";
name = "Popeye";
@@ -46363,9 +46356,6 @@
"bwK" = (
/obj/structure/flora/grass/jungle/b,
/obj/structure/flora/ausbushes/sparsegrass,
-/obj/machinery/airalarm{
- pixel_y = 22
- },
/turf/open/floor/grass,
/area/chapel/main)
"bwL" = (
@@ -48132,16 +48122,6 @@
},
/turf/open/floor/plasteel/showroomfloor,
/area/security/brig)
-"bzx" = (
-/obj/effect/turf_decal/tile/neutral,
-/obj/effect/turf_decal/tile/neutral{
- dir = 1
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 4
- },
-/turf/closed/wall,
-/area/crew_quarters/locker)
"bzy" = (
/obj/machinery/light/small{
dir = 8
@@ -58156,10 +58136,6 @@
icon_state = "plant-02";
pixel_y = 3
},
-/obj/machinery/airalarm{
- dir = 4;
- pixel_x = -22
- },
/obj/machinery/camera{
c_tag = "Prison Wing Cells";
dir = 4;
@@ -61358,7 +61334,7 @@
dir = 10
},
/turf/open/floor/plasteel,
-/area/space)
+/area/security/warden)
"bUs" = (
/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/command/glass{
@@ -67210,10 +67186,6 @@
/obj/structure/cable{
icon_state = "1-4"
},
-/obj/machinery/airalarm{
- dir = 1;
- pixel_y = -22
- },
/turf/open/floor/plasteel,
/area/security/main)
"cdU" = (
@@ -71327,9 +71299,9 @@
icon_gib = "magicarp_gib";
icon_living = "magicarp";
icon_state = "magicarp";
+ maxHealth = 200;
max_co2 = 5;
max_tox = 2;
- maxHealth = 200;
melee_damage_lower = 15;
melee_damage_upper = 20;
min_oxy = 5;
@@ -83281,10 +83253,6 @@
/obj/machinery/atmospherics/pipe/simple/yellow/visible{
dir = 5
},
-/obj/machinery/airalarm{
- dir = 4;
- pixel_x = -23
- },
/obj/machinery/camera{
c_tag = "Atmospherics Port Tanks";
dir = 4;
@@ -108992,7 +108960,7 @@ bss
btV
aEu
bxW
-bzx
+bIV
bKq
bKq
bKq
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 958ab2dc5a..c2a0e5c353 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -1111,10 +1111,6 @@
/turf/open/floor/plasteel,
/area/security/prison)
"acx" = (
-/obj/machinery/airalarm{
- dir = 8;
- pixel_x = 24
- },
/obj/machinery/cryopod{
dir = 8
},
@@ -40497,10 +40493,9 @@
/area/maintenance/port)
"bBr" = (
/obj/machinery/airalarm{
- dir = 4;
- pixel_x = -23
+ dir = 1;
+ pixel_y = -22
},
-/obj/structure/displaycase/trophy,
/turf/open/floor/wood,
/area/library)
"bBs" = (
@@ -45866,10 +45861,6 @@
/area/library)
"bML" = (
/obj/machinery/light/small,
-/obj/machinery/airalarm{
- dir = 1;
- pixel_y = -22
- },
/turf/open/floor/wood,
/area/library)
"bMM" = (
@@ -59581,6 +59572,10 @@
/obj/structure/chair{
dir = 1
},
+/obj/machinery/airalarm{
+ dir = 4;
+ pixel_x = -23
+ },
/turf/open/floor/plasteel/dark,
/area/medical/surgery)
"coz" = (
@@ -60135,9 +60130,6 @@
/obj/structure/cable/yellow{
icon_state = "4-8"
},
-/obj/machinery/atmospherics/pipe/simple/supply/hidden{
- dir = 4
- },
/obj/structure/disposalpipe/junction/flip{
dir = 8
},
@@ -60145,6 +60137,9 @@
/obj/effect/turf_decal/tile/purple{
dir = 4
},
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 1
+ },
/turf/open/floor/plasteel/white,
/area/science/research)
"cpx" = (
@@ -60947,9 +60942,6 @@
/obj/structure/cable/yellow{
icon_state = "1-2"
},
-/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
- dir = 1
- },
/obj/structure/disposalpipe/segment,
/obj/effect/turf_decal/tile/purple,
/obj/effect/turf_decal/tile/purple{
@@ -60958,6 +60950,10 @@
/obj/effect/turf_decal/tile/purple{
dir = 8
},
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
/turf/open/floor/plasteel/white,
/area/science/research)
"cqP" = (
@@ -61465,7 +61461,6 @@
/obj/structure/cable/yellow{
icon_state = "1-2"
},
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/machinery/door/firedoor,
/obj/structure/disposalpipe/segment,
/obj/machinery/door/airlock/research{
@@ -61477,6 +61472,7 @@
name = "biohazard containment shutters"
},
/obj/effect/turf_decal/delivery,
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plasteel,
/area/science/storage)
"crU" = (
@@ -61595,10 +61591,6 @@
/turf/open/floor/plasteel/white,
/area/medical/surgery)
"csl" = (
-/obj/machinery/airalarm{
- dir = 1;
- pixel_y = -22
- },
/obj/structure/window/reinforced{
dir = 8
},
@@ -62156,11 +62148,11 @@
/obj/structure/cable/yellow{
icon_state = "1-2"
},
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/disposalpipe/segment,
/obj/effect/turf_decal/stripes/line{
dir = 9
},
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plasteel,
/area/science/storage)
"cth" = (
@@ -62531,24 +62523,21 @@
/obj/structure/cable/yellow{
icon_state = "1-4"
},
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
- dir = 5
- },
/obj/structure/disposalpipe/segment{
dir = 5
},
/obj/effect/turf_decal/stripes/line{
dir = 8
},
+/obj/machinery/atmospherics/components/unary/vent_pump/on{
+ dir = 1
+ },
/turf/open/floor/plasteel,
/area/science/storage)
"ctY" = (
/obj/structure/cable/yellow{
icon_state = "4-8"
},
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
- dir = 10
- },
/obj/structure/chair/stool,
/obj/structure/disposalpipe/segment{
dir = 10
@@ -62568,10 +62557,6 @@
/area/science/storage)
"cua" = (
/obj/machinery/portable_atmospherics/canister/oxygen,
-/obj/machinery/airalarm{
- dir = 8;
- pixel_x = 24
- },
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel,
/area/science/storage)
@@ -63127,7 +63112,6 @@
/turf/open/floor/plasteel,
/area/science/storage)
"cuU" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/disposalpipe/segment,
/obj/effect/turf_decal/stripes/line{
dir = 4
@@ -63668,12 +63652,12 @@
/turf/open/floor/plasteel,
/area/science/storage)
"cvW" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/disposalpipe/segment,
/obj/effect/landmark/blobstart,
/obj/effect/turf_decal/stripes/line{
dir = 4
},
+/obj/machinery/atmospherics/components/unary/vent_scrubber/on,
/turf/open/floor/plasteel,
/area/science/storage)
"cvX" = (
@@ -68931,13 +68915,13 @@
pixel_x = -25;
pixel_y = -5
},
-/obj/machinery/airalarm{
- dir = 1;
- pixel_y = -22
- },
/obj/effect/turf_decal/stripes/corner{
dir = 4
},
+/obj/machinery/airalarm/unlocked{
+ dir = 1;
+ pixel_y = -22
+ },
/turf/open/floor/plasteel/white,
/area/science/mixing)
"cGm" = (
@@ -102705,7 +102689,7 @@ bue
bwa
bxU
bzD
-bBr
+dmD
bSx
bEw
bzE
@@ -103740,7 +103724,7 @@ bGs
bHS
bzE
bLk
-bzE
+bBr
bue
bPS
bPR
@@ -117641,7 +117625,7 @@ cmQ
cok
cpy
cqQ
-cgq
+crR
cti
ctZ
cuV
diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm
index f4d798511f..6f7f211b3e 100644
--- a/_maps/map_files/OmegaStation/OmegaStation.dmm
+++ b/_maps/map_files/OmegaStation/OmegaStation.dmm
@@ -4924,11 +4924,11 @@
/obj/effect/turf_decal/stripes/end{
dir = 1
},
-/obj/item/gun/energy/e_gun/hos,
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
+/obj/item/gun/energy/e_gun/hos,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/heads/captain/private)
"ahI" = (
@@ -40544,6 +40544,9 @@
name = "Starboard Quater Maintenance APC";
pixel_y = -26
},
+/obj/structure/cable/white{
+ icon_state = "0-8"
+ },
/turf/open/floor/plating{
icon_state = "panelscorched"
},
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index 12ae0092f0..12c507c2bf 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -16530,6 +16530,9 @@
/obj/structure/cable{
icon_state = "4-8"
},
+/obj/machinery/airalarm{
+ pixel_y = 22
+ },
/turf/open/floor/plasteel,
/area/quartermaster/warehouse)
"aMr" = (
@@ -47631,6 +47634,9 @@
pixel_x = -25;
specialfunctions = 4
},
+/obj/machinery/airalarm{
+ pixel_y = 22
+ },
/turf/open/floor/plasteel/grimy,
/area/chapel/main/monastery)
"cgM" = (
@@ -47979,6 +47985,9 @@
pixel_x = -25;
specialfunctions = 4
},
+/obj/machinery/airalarm{
+ pixel_y = 22
+ },
/turf/open/floor/plasteel/grimy,
/area/chapel/main/monastery)
"cip" = (
@@ -51466,6 +51475,10 @@
name = "Coffin Storage";
req_one_access_txt = "22"
},
+/obj/machinery/airalarm{
+ dir = 8;
+ pixel_x = 24
+ },
/turf/open/floor/plasteel/dark,
/area/chapel/main/monastery)
"cvu" = (
@@ -59434,6 +59447,10 @@
/obj/effect/turf_decal/tile/yellow{
dir = 8
},
+/obj/machinery/airalarm{
+ dir = 8;
+ pixel_x = 23
+ },
/turf/open/floor/plasteel,
/area/construction/mining/aux_base)
"qXq" = (
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index c9decee834..0000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-version: '{build}'
-skip_branch_with_pr: true
-shallow_clone: true
-branches:
- except:
- - ___TGS3TempBranch
- - ___TGSTempBranch
-cache:
- - C:\byond\ -> dependencies.sh
-build_script:
- - ps: tools/appveyor/build.ps1
- - ps: "$deployPath = $env:APPVEYOR_BUILD_FOLDER + '/deploy'; bash tools/deploy.sh $deployPath"
- - ps: "[System.IO.Compression.ZipFile]::CreateFromDirectory($env:APPVEYOR_BUILD_FOLDER + '/deploy', $env:APPVEYOR_BUILD_FOLDER + '/deploy.zip')"
-artifacts:
- - path: deploy.zip
diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm
index bad64846d6..cad75fbfe4 100644
--- a/code/__DEFINES/MC.dm
+++ b/code/__DEFINES/MC.dm
@@ -22,49 +22,45 @@
#define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum}
#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum;Processor.currentrun -= Datum
-//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
+//! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
-//subsystem does not initialize.
-#define SS_NO_INIT (1<<0)
+/// subsystem does not initialize.
+#define SS_NO_INIT 1
-//subsystem does not fire.
-// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
-// (Requires a MC restart to change)
-#define SS_NO_FIRE (1<<1)
+/** subsystem does not fire. */
+/// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
+/// (Requires a MC restart to change)
+#define SS_NO_FIRE 2
-//subsystem only runs on spare cpu (after all non-background subsystems have ran that tick)
-// SS_BACKGROUND has its own priority bracket
-#define SS_BACKGROUND (1<<2)
+/** Subsystem only runs on spare cpu (after all non-background subsystems have ran that tick) */
+/// SS_BACKGROUND has its own priority bracket, this overrides SS_TICKER's priority bump
+#define SS_BACKGROUND 4
-//subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
-#define SS_NO_TICK_CHECK (1<<3)
+/// subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
+#define SS_NO_TICK_CHECK 8
-//Treat wait as a tick count, not DS, run every wait ticks.
-// (also forces it to run first in the tick, above even SS_NO_TICK_CHECK subsystems)
-// (implies all runlevels because of how it works)
-// (overrides SS_BACKGROUND)
-// This is designed for basically anything that works as a mini-mc (like SStimer)
-#define SS_TICKER (1<<4)
+/** Treat wait as a tick count, not DS, run every wait ticks. */
+/// (also forces it to run first in the tick (unless SS_BACKGROUND))
+/// (implies all runlevels because of how it works)
+/// This is designed for basically anything that works as a mini-mc (like SStimer)
+#define SS_TICKER 16
-//keep the subsystem's timing on point by firing early if it fired late last fire because of lag
-// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
-#define SS_KEEP_TIMING (1<<5)
+/** keep the subsystem's timing on point by firing early if it fired late last fire because of lag */
+/// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
+#define SS_KEEP_TIMING 32
-//Calculate its next fire after its fired.
-// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
-// This flag overrides SS_KEEP_TIMING
-#define SS_POST_FIRE_TIMING (1<<6)
+/** Calculate its next fire after its fired. */
+/// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
+/// This flag overrides SS_KEEP_TIMING
+#define SS_POST_FIRE_TIMING 64
-/// Show in stat() by default even if SS_NO_FIRE
-#define SS_ALWAYS_SHOW_STAT (1<<7)
-
-//SUBSYSTEM STATES
-#define SS_IDLE 0 //aint doing shit.
-#define SS_QUEUED 1 //queued to run
-#define SS_RUNNING 2 //actively running
-#define SS_PAUSED 3 //paused by mc_tick_check
-#define SS_SLEEPING 4 //fire() slept.
-#define SS_PAUSING 5 //in the middle of pausing
+//! SUBSYSTEM STATES
+#define SS_IDLE 0 /// ain't doing shit.
+#define SS_QUEUED 1 /// queued to run
+#define SS_RUNNING 2 /// actively running
+#define SS_PAUSED 3 /// paused by mc_tick_check
+#define SS_SLEEPING 4 /// fire() slept.
+#define SS_PAUSING 5 /// in the middle of pausing
#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\
/datum/controller/subsystem/##X/New(){\
diff --git a/code/__DEFINES/_extools.dm b/code/__DEFINES/_extools.dm
index 4513243aae..e01e09a3d3 100644
--- a/code/__DEFINES/_extools.dm
+++ b/code/__DEFINES/_extools.dm
@@ -1 +1,38 @@
-#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so")
+// _extools_api.dm - DM API for extools extension library
+// (blatently stolen from rust_g)
+//
+// To configure, create a `extools.config.dm` and set what you care about from
+// the following options:
+//
+// #define EXTOOLS "path/to/extools"
+// Override the .dll/.so detection logic with a fixed path or with detection
+// logic of your own.
+
+#ifndef EXTOOLS
+// Default automatic EXTOOLS detection.
+// On Windows, looks in the standard places for `byond-extools.dll`.
+// On Linux, looks in the standard places for`libbyond-extools.so`.
+
+/* This comment bypasses grep checks */ /var/__extools
+
+/proc/__detect_extools()
+ if (world.system_type == UNIX)
+ if (fexists("./libbyond-extools.so"))
+ // No need for LD_LIBRARY_PATH badness.
+ return __extools = "./libbyond-extools.so"
+ else
+ // It's not in the current directory, so try others
+ return __extools = "libbyond-extools.so"
+ else
+ return __extools = "byond-extools.dll"
+
+#define EXTOOLS (__extools || __detect_extools())
+#endif
+
+#ifndef UNIT_TESTS // use default logging as extools is broken on travis
+#define EXTOOLS_LOGGING // rust_g is used as a fallback if this is undefined
+#endif
+
+/proc/extools_log_write()
+
+/proc/extools_finalize_logging()
diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm
index 6e018b1eeb..f12e3618f7 100644
--- a/code/__DEFINES/_flags/_flags.dm
+++ b/code/__DEFINES/_flags/_flags.dm
@@ -163,5 +163,19 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
else if(!HAS_TRAIT(x, TRAIT_KEEP_TOGETHER))\
x.appearance_flags &= ~KEEP_TOGETHER
+//dir macros
+///Returns true if the dir is diagonal, false otherwise
+#define ISDIAGONALDIR(d) (d&(d-1))
+///True if the dir is north or south, false therwise
+#define NSCOMPONENT(d) (d&(NORTH|SOUTH))
+///True if the dir is east/west, false otherwise
+#define EWCOMPONENT(d) (d&(EAST|WEST))
+///Flips the dir for north/south directions
+#define NSDIRFLIP(d) (d^(NORTH|SOUTH))
+///Flips the dir for east/west directions
+#define EWDIRFLIP(d) (d^(EAST|WEST))
+///Turns the dir by 180 degrees
+#define DIRFLIP(d) turn(d, 180)
+
/// 33554431 (2^24 - 1) is the maximum value our bitflags can reach.
#define MAX_BITFLAG_DIGITS 8
diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index 143063b4e9..f6293454ee 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -91,3 +91,6 @@
#define SPAM_TRIGGER_WARNING 5 //Number of identical messages required before the spam-prevention will warn you to stfu
#define SPAM_TRIGGER_AUTOMUTE 10 //Number of identical messages required before the spam-prevention will automute you
+
+#define STICKYBAN_DB_CACHE_TIME 10 SECONDS
+#define STICKYBAN_ROGUE_CHECK_TIME 5
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 68515426c3..e71243994d 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -69,6 +69,9 @@
#define LINGBLOOD_EXPLOSION_THRESHOLD (LINGBLOOD_DETECTION_THRESHOLD * LINGBLOOD_EXPLOSION_MULT) //Hey, important to note here: the explosion threshold is explicitly more than, rather than more than or equal to. This stops a single loud ability from triggering the explosion threshold.
///Heretics --
+GLOBAL_LIST_EMPTY(living_heart_cache) //A list of all living hearts in existance, for us to iterate through.
+
+
#define IS_HERETIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic))
#define IS_HERETIC_MONSTER(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic_monster))
diff --git a/code/__DEFINES/dcs/helpers.dm b/code/__DEFINES/dcs/helpers.dm
index 182035db9b..ba2b9a704a 100644
--- a/code/__DEFINES/dcs/helpers.dm
+++ b/code/__DEFINES/dcs/helpers.dm
@@ -6,9 +6,16 @@
#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
+/// Signifies that this proc is used to handle signals.
+/// Every proc you pass to RegisterSignal must have this.
+#define SIGNAL_HANDLER SHOULD_NOT_SLEEP(TRUE)
+
+/// Signifies that this proc is used to handle signals, but also sleeps.
+/// Do not use this for new work.
+#define SIGNAL_HANDLER_DOES_SLEEP
+
/// A wrapper for _AddElement that allows us to pretend we're using normal named arguments
#define AddElement(arguments...) _AddElement(list(##arguments))
-
/// A wrapper for _RemoveElement that allows us to pretend we're using normal named arguments
#define RemoveElement(arguments...) _RemoveElement(list(##arguments))
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index a084f2ae34..a1bb4122a8 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -541,6 +541,9 @@
#define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" //from turf AltClickOn(): (/mob)
#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob)
+// /datum/element/ventcrawling signals
+#define COMSIG_HANDLE_VENTCRAWL "handle_ventcrawl" //when atom with ventcrawling element attempts to ventcrawl
+#define COMSIG_CHECK_VENTCRAWL "check_ventcrawl" //to check an atom's ventcrawling element tier (if applicable)
// twitch plays
/// Returns direction: (wipe_votes)
#define COMSIG_TWITCH_PLAYS_MOVEMENT_DATA "twitch_plays_movement_data"
diff --git a/code/__DEFINES/loadout.dm b/code/__DEFINES/loadout.dm
index ecd043a66a..973457692e 100644
--- a/code/__DEFINES/loadout.dm
+++ b/code/__DEFINES/loadout.dm
@@ -72,9 +72,11 @@
#define LOADOUT_LIMBS list(LOADOUT_LIMB_NORMAL,LOADOUT_LIMB_PROSTHETIC,LOADOUT_LIMB_AMPUTATED) //you can amputate your legs/arms though
//loadout saving/loading specific defines
-#define MAXIMUM_LOADOUT_SAVES 5
-#define LOADOUT_ITEM "loadout_item"
-#define LOADOUT_COLOR "loadout_color"
+#define MAXIMUM_LOADOUT_SAVES 5
+#define LOADOUT_ITEM "loadout_item"
+#define LOADOUT_COLOR "loadout_color"
+#define LOADOUT_CUSTOM_NAME "loadout_custom_name"
+#define LOADOUT_CUSTOM_DESCRIPTION "loadout_custom_description"
//loadout item flags
#define LOADOUT_CAN_NAME (1<<0) //renaming items
diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm
new file mode 100644
index 0000000000..5bf7de8647
--- /dev/null
+++ b/code/__DEFINES/movement.dm
@@ -0,0 +1,26 @@
+/// The minimum for glide_size to be clamped to.
+#define MIN_GLIDE_SIZE 1
+/// The maximum for glide_size to be clamped to.
+/// This shouldn't be higher than the icon size, and generally you shouldn't be changing this, but it's here just in case.
+#define MAX_GLIDE_SIZE 32
+
+/// Compensating for time dialation
+GLOBAL_VAR_INIT(glide_size_multiplier, 1.0)
+
+///Broken down, here's what this does:
+/// divides the world icon_size (32) by delay divided by ticklag to get the number of pixels something should be moving each tick.
+/// The division result is given a min value of 1 to prevent obscenely slow glide sizes from being set
+/// Then that's multiplied by the global glide size multiplier. 1.25 by default feels pretty close to spot on. This is just to try to get byond to behave.
+/// The whole result is then clamped to within the range above.
+/// Not very readable but it works
+#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((32 / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE))
+
+/// Enables smooth movement
+// #define SMOOTH_MOVEMENT
+
+/// Set appearance flags in vars
+#ifdef SMOOTH_MOVEMENT
+ #define SET_APPEARANCE_FLAGS(_flags) appearance_flags = (_flags | LONG_GLIDE)
+#else
+ #define SET_APPEARANCE_FLAGS(_flags) appearance_flags = _flags
+#endif
diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm
index 20e7975ec4..988acd3dae 100644
--- a/code/__DEFINES/rust_g.dm
+++ b/code/__DEFINES/rust_g.dm
@@ -1,19 +1,76 @@
// rust_g.dm - DM API for rust_g extension library
-#define RUST_G "rust_g"
+//
+// To configure, create a `rust_g.config.dm` and set what you care about from
+// the following options:
+//
+// #define RUST_G "path/to/rust_g"
+// Override the .dll/.so detection logic with a fixed path or with detection
+// logic of your own.
+//
+// #define RUSTG_OVERRIDE_BUILTINS
+// Enable replacement rust-g functions for certain builtins. Off by default.
-#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
-#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
-#define RUSTG_JOB_ERROR "JOB PANICKED"
+#ifndef RUST_G
+// Default automatic RUST_G detection.
+// On Windows, looks in the standard places for `rust_g.dll`.
+// On Linux, looks in `.`, `$LD_LIBRARY_PATH`, and `~/.byond/bin` for either of
+// `librust_g.so` (preferred) or `rust_g` (old).
+
+/* This comment bypasses grep checks */ /var/__rust_g
+
+/proc/__detect_rust_g()
+ if (world.system_type == UNIX)
+ if (fexists("./librust_g.so"))
+ // No need for LD_LIBRARY_PATH badness.
+ return __rust_g = "./librust_g.so"
+ else if (fexists("./rust_g"))
+ // Old dumb filename.
+ return __rust_g = "./rust_g"
+ else if (fexists("[world.GetConfig("env", "HOME")]/.byond/bin/rust_g"))
+ // Old dumb filename in `~/.byond/bin`.
+ return __rust_g = "rust_g"
+ else
+ // It's not in the current directory, so try others
+ return __rust_g = "librust_g.so"
+ else
+ return __rust_g = "rust_g"
+
+#define RUST_G (__rust_g || __detect_rust_g())
+#endif
+
+/**
+ * This proc generates a cellular automata noise grid which can be used in procedural generation methods.
+ *
+ * Returns a single string that goes row by row, with values of 1 representing an alive cell, and a value of 0 representing a dead cell.
+ *
+ * Arguments:
+ * * percentage: The chance of a turf starting closed
+ * * smoothing_iterations: The amount of iterations the cellular automata simulates before returning the results
+ * * birth_limit: If the number of neighboring cells is higher than this amount, a cell is born
+ * * death_limit: If the number of neighboring cells is lower than this amount, a cell dies
+ * * width: The width of the grid.
+ * * height: The height of the grid.
+ */
+#define rustg_cnoise_generate(percentage, smoothing_iterations, birth_limit, death_limit, width, height) \
+ call(RUST_G, "cnoise_generate")(percentage, smoothing_iterations, birth_limit, death_limit, width, height)
#define rustg_dmi_strip_metadata(fname) call(RUST_G, "dmi_strip_metadata")(fname)
#define rustg_dmi_create_png(path, width, height, data) call(RUST_G, "dmi_create_png")(path, width, height, data)
+#define rustg_dmi_resize_png(path, width, height, resizetype) call(RUST_G, "dmi_resize_png")(path, width, height, resizetype)
+
+#define rustg_file_read(fname) call(RUST_G, "file_read")(fname)
+#define rustg_file_exists(fname) call(RUST_G, "file_exists")(fname)
+#define rustg_file_write(text, fname) call(RUST_G, "file_write")(text, fname)
+#define rustg_file_append(text, fname) call(RUST_G, "file_append")(text, fname)
+
+#ifdef RUSTG_OVERRIDE_BUILTINS
+ #define file2text(fname) rustg_file_read("[fname]")
+ #define text2file(text, fname) rustg_file_append(text, "[fname]")
+#endif
#define rustg_git_revparse(rev) call(RUST_G, "rg_git_revparse")(rev)
#define rustg_git_commit_date(rev) call(RUST_G, "rg_git_commit_date")(rev)
-#define rustg_log_write(fname, text, format) call(RUST_G, "log_write")(fname, text, format)
-/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()
-
#define RUSTG_HTTP_METHOD_GET "get"
#define RUSTG_HTTP_METHOD_PUT "put"
#define RUSTG_HTTP_METHOD_DELETE "delete"
@@ -23,3 +80,22 @@
#define rustg_http_request_blocking(method, url, body, headers) call(RUST_G, "http_request_blocking")(method, url, body, headers)
#define rustg_http_request_async(method, url, body, headers) call(RUST_G, "http_request_async")(method, url, body, headers)
#define rustg_http_check_request(req_id) call(RUST_G, "http_check_request")(req_id)
+
+#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
+#define RUSTG_JOB_NO_SUCH_JOB "NO SUCH JOB"
+#define RUSTG_JOB_ERROR "JOB PANICKED"
+
+#define rustg_json_is_valid(text) (call(RUST_G, "json_is_valid")(text) == "true")
+
+#define rustg_log_write(fname, text, format) call(RUST_G, "log_write")(fname, text, format)
+/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()
+
+#define rustg_noise_get_at_coordinates(seed, x, y) call(RUST_G, "noise_get_at_coordinates")(seed, x, y)
+
+#define rustg_sql_connect_pool(options) call(RUST_G, "sql_connect_pool")(options)
+#define rustg_sql_query_async(handle, query, params) call(RUST_G, "sql_query_async")(handle, query, params)
+#define rustg_sql_query_blocking(handle, query, params) call(RUST_G, "sql_query_blocking")(handle, query, params)
+#define rustg_sql_connected(handle) call(RUST_G, "sql_connected")(handle)
+#define rustg_sql_disconnect_pool(handle) call(RUST_G, "sql_disconnect_pool")(handle)
+#define rustg_sql_check_query(job_id) call(RUST_G, "sql_check_query")("[job_id]")
+
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index 73781154c5..1098a07b39 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -3,6 +3,7 @@
#define CHANNEL_ADMIN 1023
#define CHANNEL_VOX 1022
#define CHANNEL_JUKEBOX 1021
+
#define CHANNEL_JUKEBOX_START 1016 //The gap between this and CHANNEL_JUKEBOX determines the amount of free jukebox channels. This currently allows 6 jukebox channels to exist.
#define CHANNEL_JUSTICAR_ARK 1015
#define CHANNEL_HEARTBEAT 1014 //sound channel for heartbeats
@@ -15,6 +16,17 @@
#define CHANNEL_DIGEST 1009
#define CHANNEL_PREYLOOP 1008
+///Default range of a sound.
+#define SOUND_RANGE 17
+///default extra range for sounds considered to be quieter
+#define SHORT_RANGE_SOUND_EXTRARANGE -9
+///The range deducted from sound range for things that are considered silent / sneaky
+#define SILENCED_SOUND_EXTRARANGE -11
+///Percentage of sound's range where no falloff is applied
+#define SOUND_DEFAULT_FALLOFF_DISTANCE 1 //For a normal sound this would be 1 tile of no falloff
+///The default exponent of sound falloff
+#define SOUND_FALLOFF_EXPONENT 6
+
//THIS SHOULD ALWAYS BE THE LOWEST ONE!
//KEEP IT UPDATED
@@ -23,6 +35,7 @@
#define MAX_INSTRUMENT_CHANNELS (128 * 6)
#define SOUND_MINIMUM_PRESSURE 10
+/// remove
#define FALLOFF_SOUNDS 1
@@ -53,7 +66,8 @@
#define MINING list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\
'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
- 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint1.ogg', 'sound/ambience/ambilava.ogg')
+ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint1.ogg',\
+ 'sound/ambience/ambilava.ogg')
#define MEDICAL list('sound/ambience/ambinice.ogg')
@@ -80,3 +94,55 @@
'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg')
+
+
+#define INTERACTION_SOUND_RANGE_MODIFIER -3
+#define EQUIP_SOUND_VOLUME 30
+#define PICKUP_SOUND_VOLUME 15
+#define DROP_SOUND_VOLUME 20
+#define YEET_SOUND_VOLUME 90
+
+
+//default byond sound environments
+#define SOUND_ENVIRONMENT_NONE -1
+#define SOUND_ENVIRONMENT_GENERIC 0
+#define SOUND_ENVIRONMENT_PADDED_CELL 1
+#define SOUND_ENVIRONMENT_ROOM 2
+#define SOUND_ENVIRONMENT_BATHROOM 3
+#define SOUND_ENVIRONMENT_LIVINGROOM 4
+#define SOUND_ENVIRONMENT_STONEROOM 5
+#define SOUND_ENVIRONMENT_AUDITORIUM 6
+#define SOUND_ENVIRONMENT_CONCERT_HALL 7
+#define SOUND_ENVIRONMENT_CAVE 8
+#define SOUND_ENVIRONMENT_ARENA 9
+#define SOUND_ENVIRONMENT_HANGAR 10
+#define SOUND_ENVIRONMENT_CARPETED_HALLWAY 11
+#define SOUND_ENVIRONMENT_HALLWAY 12
+#define SOUND_ENVIRONMENT_STONE_CORRIDOR 13
+#define SOUND_ENVIRONMENT_ALLEY 14
+#define SOUND_ENVIRONMENT_FOREST 15
+#define SOUND_ENVIRONMENT_CITY 16
+#define SOUND_ENVIRONMENT_MOUNTAINS 17
+#define SOUND_ENVIRONMENT_QUARRY 18
+#define SOUND_ENVIRONMENT_PLAIN 19
+#define SOUND_ENVIRONMENT_PARKING_LOT 20
+#define SOUND_ENVIRONMENT_SEWER_PIPE 21
+#define SOUND_ENVIRONMENT_UNDERWATER 22
+#define SOUND_ENVIRONMENT_DRUGGED 23
+#define SOUND_ENVIRONMENT_DIZZY 24
+#define SOUND_ENVIRONMENT_PSYCHOTIC 25
+//If we ever make custom ones add them here
+
+//"sound areas": easy way of keeping different types of areas consistent.
+#define SOUND_AREA_STANDARD_STATION SOUND_ENVIRONMENT_PARKING_LOT
+#define SOUND_AREA_LARGE_ENCLOSED SOUND_ENVIRONMENT_QUARRY
+#define SOUND_AREA_SMALL_ENCLOSED SOUND_ENVIRONMENT_BATHROOM
+#define SOUND_AREA_TUNNEL_ENCLOSED SOUND_ENVIRONMENT_STONEROOM
+#define SOUND_AREA_LARGE_SOFTFLOOR SOUND_ENVIRONMENT_CARPETED_HALLWAY
+#define SOUND_AREA_MEDIUM_SOFTFLOOR SOUND_ENVIRONMENT_LIVINGROOM
+#define SOUND_AREA_SMALL_SOFTFLOOR SOUND_ENVIRONMENT_ROOM
+#define SOUND_AREA_ASTEROID SOUND_ENVIRONMENT_CAVE
+#define SOUND_AREA_SPACE SOUND_ENVIRONMENT_UNDERWATER
+#define SOUND_AREA_LAVALAND SOUND_ENVIRONMENT_MOUNTAINS
+#define SOUND_AREA_ICEMOON SOUND_ENVIRONMENT_CAVE
+#define SOUND_AREA_WOODFLOOR SOUND_ENVIRONMENT_CITY
diff --git a/code/__DEFINES/spaceman_dmm.dm b/code/__DEFINES/spaceman_dmm.dm
index e21f3dc1c1..087fa5e6e6 100644
--- a/code/__DEFINES/spaceman_dmm.dm
+++ b/code/__DEFINES/spaceman_dmm.dm
@@ -29,5 +29,5 @@
#endif
/world/proc/enable_debugger()
- if (fexists(EXTOOLS))
- call(EXTOOLS, "debug_initialize")()
+ if (fexists(EXTOOLS))
+ call(EXTOOLS, "debug_initialize")()
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index b405db83b0..cbf701e1d3 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -1,40 +1,90 @@
-//Update this whenever the db schema changes
-//make sure you add an update to the schema_version stable in the db changelog
+//! Defines for subsystems and overlays
+//!
+//! Lots of important stuff in here, make sure you have your brain switched on
+//! when editing this file
+
+//! ## DB defines
+/**
+ * DB major schema version
+ *
+ * Update this whenever the db schema changes
+ *
+ * make sure you add an update to the schema_version stable in the db changelog
+ */
#define DB_MAJOR_VERSION 4
+
+/**
+ * DB minor schema version
+ *
+ * Update this whenever the db schema changes
+ *
+ * make sure you add an update to the schema_version stable in the db changelog
+ */
#define DB_MINOR_VERSION 7
-//Timing subsystem
-//Don't run if there is an identical unique timer active
-//if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer, and returns the id of the existing timer
+//! ## Timing subsystem
+/**
+ * Don't run if there is an identical unique timer active
+ *
+ * if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer,
+ * and returns the id of the existing timer
+ */
#define TIMER_UNIQUE (1<<0)
-//For unique timers: Replace the old timer rather then not start this one
+
+///For unique timers: Replace the old timer rather then not start this one
#define TIMER_OVERRIDE (1<<1)
-//Timing should be based on how timing progresses on clients, not the sever.
-// tracking this is more expensive,
-// should only be used in conjuction with things that have to progress client side, such as animate() or sound()
+
+/**
+ * Timing should be based on how timing progresses on clients, not the server.
+ *
+ * Tracking this is more expensive,
+ * should only be used in conjuction with things that have to progress client side, such as
+ * animate() or sound()
+ */
#define TIMER_CLIENT_TIME (1<<2)
-//Timer can be stopped using deltimer()
+
+///Timer can be stopped using deltimer()
#define TIMER_STOPPABLE (1<<3)
-//To be used with TIMER_UNIQUE
-//prevents distinguishing identical timers with the wait variable
+
+///prevents distinguishing identical timers with the wait variable
+///
+///To be used with TIMER_UNIQUE
#define TIMER_NO_HASH_WAIT (1<<4)
-//Loops the timer repeatedly until qdeleted
-//In most cases you want a subsystem instead
+
+///Loops the timer repeatedly until qdeleted
+///
+///In most cases you want a subsystem instead, so don't use this unless you have a good reason
#define TIMER_LOOP (1<<5)
-#define TIMER_NO_INVOKE_WARNING 600 //number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
-
+///Empty ID define
#define TIMER_ID_NULL -1
-#define INITIALIZATION_INSSATOMS 0 //New should not call Initialize
-#define INITIALIZATION_INNEW_MAPLOAD 2 //New should call Initialize(TRUE)
-#define INITIALIZATION_INNEW_REGULAR 1 //New should call Initialize(FALSE)
+//! ## Initialization subsystem
-#define INITIALIZE_HINT_NORMAL 0 //Nothing happens
-#define INITIALIZE_HINT_LATELOAD 1 //Call LateInitialize
-#define INITIALIZE_HINT_QDEL 2 //Call qdel on the atom
+///New should not call Initialize
+#define INITIALIZATION_INSSATOMS 0
+///New should call Initialize(TRUE)
+#define INITIALIZATION_INNEW_MAPLOAD 2
+///New should call Initialize(FALSE)
+#define INITIALIZATION_INNEW_REGULAR 1
-//type and all subtypes should always call Initialize in New()
+//! ### Initialization hints
+
+///Nothing happens
+#define INITIALIZE_HINT_NORMAL 0
+/**
+ * call LateInitialize at the end of all atom Initalization
+ *
+ * The item will be added to the late_loaders list, this is iterated over after
+ * initalization of subsystems is complete and calls LateInitalize on the atom
+ * see [this file for the LateIntialize proc](atom.html#proc/LateInitialize)
+ */
+#define INITIALIZE_HINT_LATELOAD 1
+
+///Call qdel on the atom after intialization
+#define INITIALIZE_HINT_QDEL 2
+
+///type and all subtypes should always immediately call Initialize in New()
#define INITIALIZE_IMMEDIATE(X) ##X/New(loc, ...){\
..();\
if(!(flags_1 & INITIALIZED_1)) {\
@@ -47,35 +97,40 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
-#define INIT_ORDER_PROFILER 100
-#define INIT_ORDER_FAIL2TOPIC 99
-#define INIT_ORDER_TITLE 98
-#define INIT_ORDER_GARBAGE 95
-#define INIT_ORDER_DBCORE 94
-#define INIT_ORDER_STATPANELS 93
-#define INIT_ORDER_BLACKBOX 92
-#define INIT_ORDER_SERVER_MAINT 91
-#define INIT_ORDER_INPUT 90
-#define INIT_ORDER_SOUNDS 85
+#define INIT_ORDER_PROFILER 102
+#define INIT_ORDER_FAIL2TOPIC 101
+#define INIT_ORDER_TITLE 100
+#define INIT_ORDER_GARBAGE 99
+#define INIT_ORDER_DBCORE 95
+#define INIT_ORDER_BLACKBOX 94
+#define INIT_ORDER_SERVER_MAINT 93
+#define INIT_ORDER_INPUT 85
+#define INIT_ORDER_SOUNDS 83
+#define INIT_ORDER_INSTRUMENTS 82
#define INIT_ORDER_VIS 80
+// #define INIT_ORDER_ACHIEVEMENTS 77
#define INIT_ORDER_RESEARCH 75
#define INIT_ORDER_EVENTS 70
#define INIT_ORDER_JOBS 65
#define INIT_ORDER_QUIRKS 60
#define INIT_ORDER_TICKER 55
-#define INIT_ORDER_INSTRUMENTS 53
+// #define INIT_ORDER_TCG 55
#define INIT_ORDER_MAPPING 50
-#define INIT_ORDER_ECONOMY 45
-#define INIT_ORDER_NETWORKS 40
+#define INIT_ORDER_TIMETRACK 47
+#define INIT_ORDER_NETWORKS 45
+#define INIT_ORDER_ECONOMY 40
#define INIT_ORDER_HOLODECK 35
+// #define INIT_ORDER_OUTPUTS 35
#define INIT_ORDER_ATOMS 30
#define INIT_ORDER_LANGUAGE 25
#define INIT_ORDER_MACHINES 20
#define INIT_ORDER_CIRCUIT 15
+// #define INIT_ORDER_SKILLS 15
#define INIT_ORDER_TIMER 1
#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_AIR -1
#define INIT_ORDER_AIR_TURFS -2
+#define INIT_ORDER_PERSISTENCE -2 //before assets because some assets take data from SSPersistence
#define INIT_ORDER_MINIMAP -3
#define INIT_ORDER_ASSETS -4
#define INIT_ORDER_ICON_SMOOTHING -5
@@ -86,7 +141,9 @@
#define INIT_ORDER_SHUTTLE -21
#define INIT_ORDER_MINOR_MAPPING -40
#define INIT_ORDER_PATH -50
-#define INIT_ORDER_PERSISTENCE -95
+// #define INIT_ORDER_DISCORD -60
+// #define INIT_ORDER_EXPLOSIONS -69
+#define INIT_ORDER_STATPANELS -98
#define INIT_ORDER_DEMO -99 // o avoid a bunch of changes related to initialization being written, do this last
#define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init.
@@ -102,6 +159,7 @@
#define FIRE_PRIORITY_GARBAGE 15
#define FIRE_PRIORITY_WET_FLOORS 20
#define FIRE_PRIORITY_AIR 20
+#define FIRE_PRIORITY_NPC 20
#define FIRE_PRIORITY_PROCESS 25
#define FIRE_PRIORITY_THROWING 25
#define FIRE_PRIORITY_SPACEDRIFT 30
@@ -116,7 +174,6 @@
#define FIRE_PRIORITY_AIR_TURFS 40
#define FIRE_PRIORITY_DEFAULT 50
#define FIRE_PRIORITY_PARALLAX 65
-#define FIRE_PRIORITY_NPC 80
#define FIRE_PRIORITY_MOBS 100
#define FIRE_PRIORITY_TGUI 110
#define FIRE_PRIORITY_PROJECTILES 200
@@ -126,6 +183,8 @@
#define FIRE_PRIORITY_CHAT 400
#define FIRE_PRIORITY_RUNECHAT 410
#define FIRE_PRIORITY_OVERLAYS 500
+// #define FIRE_PRIORITY_EXPLOSIONS 666
+#define FIRE_PRIORITY_TIMER 700
#define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost.
// SS runlevels
@@ -138,6 +197,37 @@
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
+
+
+//! ## Overlays subsystem
+
+///Compile all the overlays for an atom from the cache lists
+// |= on overlays is not actually guaranteed to not add same appearances but we're optimistically using it anyway.
+#define COMPILE_OVERLAYS(A)\
+ if (TRUE) {\
+ var/list/ad = A.add_overlays;\
+ var/list/rm = A.remove_overlays;\
+ if(LAZYLEN(rm)){\
+ A.overlays -= rm;\
+ rm.Cut();\
+ }\
+ if(LAZYLEN(ad)){\
+ A.overlays |= ad;\
+ ad.Cut();\
+ }\
+ A.flags_1 &= ~OVERLAY_QUEUED_1;\
+ }
+
+
+/**
+ Create a new timer and add it to the queue.
+ * Arguments:
+ * * callback the callback to call on timer finish
+ * * wait deciseconds to run the timer for
+ * * flags flags for this timer, see: code\__DEFINES\subsystems.dm
+*/
+#define addtimer(args...) _addtimer(args, file = __FILE__, line = __LINE__)
+
// SSair run section
#define SSAIR_PIPENETS 1
#define SSAIR_ATMOSMACHINERY 2
@@ -148,19 +238,3 @@
#define SSAIR_REBUILD_PIPENETS 7
#define SSAIR_EQUALIZE 8
#define SSAIR_ACTIVETURFS 9
-
-// |= on overlays is not actually guaranteed to not add same appearances but we're optimistically using it anyway.
-#define COMPILE_OVERLAYS(A)\
- if (TRUE) {\
- var/list/ad = A.add_overlays;\
- var/list/rm = A.remove_overlays;\
- if(LAZYLEN(rm)){\
- A.overlays -= rm;\
- A.remove_overlays = null;\
- }\
- if(LAZYLEN(ad)){\
- A.overlays |= ad;\
- A.add_overlays = null;\
- }\
- A.flags_1 &= ~OVERLAY_QUEUED_1;\
- }
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 2275c4b90b..b7750556d5 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -253,6 +253,7 @@
// item traits
#define TRAIT_NODROP "nodrop"
+#define TRAIT_SPOOKY_THROW "spooky_throw"
// common trait sources
#define TRAIT_GENERIC "generic"
diff --git a/code/__HELPERS/_extools_api.dm b/code/__HELPERS/_extools_api.dm
deleted file mode 100644
index af348dc939..0000000000
--- a/code/__HELPERS/_extools_api.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-#define EXTOOLS_LOGGING // rust_g is used as a fallback if this is undefined
-
-/proc/extools_log_write()
-
-/proc/extools_finalize_logging()
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index 3efb50ef65..a59ee9fcb0 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -37,7 +37,7 @@
* TYPECONT: The typepath of the contents of the list
* COMPARE: The object to compare against, usualy the same as INPUT
* COMPARISON: The variable on the objects to compare
- * COMPTYPE: How the current bin item to compare against COMPARE is fetched. By key or value.
+ * COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE.
*/
#define BINARY_INSERT(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \
do {\
@@ -49,7 +49,7 @@
var/__BIN_LEFT = 1;\
var/__BIN_RIGHT = __BIN_CTTL;\
var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
- var/##TYPECONT/__BIN_ITEM;\
+ var ##TYPECONT/__BIN_ITEM;\
while(__BIN_LEFT < __BIN_RIGHT) {\
__BIN_ITEM = COMPTYPE;\
if(__BIN_ITEM.##COMPARISON <= COMPARE.##COMPARISON) {\
@@ -67,24 +67,25 @@
//Returns a list in plain english as a string
/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
- var/total = input.len
- if (!total)
- return nothing_text
- else if (total == 1)
- return "[input[1]]"
- else if (total == 2)
- return "[input[1]][and_text][input[2]]"
- else
- var/output = ""
- var/index = 1
- while (index < total)
- if (index == total - 1)
- comma_text = final_comma_text
+ var/total = length(input)
+ switch(total)
+ if (0)
+ return "[nothing_text]"
+ if (1)
+ return "[input[1]]"
+ if (2)
+ return "[input[1]][and_text][input[2]]"
+ else
+ var/output = ""
+ var/index = 1
+ while (index < total)
+ if (index == total - 1)
+ comma_text = final_comma_text
- output += "[input[index]][comma_text]"
- index++
+ output += "[input[index]][comma_text]"
+ index++
- return "[output][and_text][input[index]]"
+ return "[output][and_text][input[index]]"
//Returns list element or null. Should prevent "index out of bounds" error.
/proc/listgetindex(list/L, index)
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 76ca97cd3a..18d02229dd 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -32,7 +32,7 @@
#define testing(msg)
#endif
-#ifdef UNIT_TESTS
+#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM)
/proc/log_test(text)
WRITE_LOG(GLOB.test_log, text)
SEND_TEXT(world.log, text)
@@ -191,6 +191,10 @@
/proc/log_mapping(text)
WRITE_LOG(GLOB.world_map_error_log, text)
+/proc/log_perf(list/perf_info)
+ . = "[perf_info.Join(",")]\n"
+ WRITE_LOG_NO_FORMAT(GLOB.perf_log, .)
+
/proc/log_reagent(text)
WRITE_LOG(GLOB.reagent_log, text)
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index a61e3a6492..8ff610e68c 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -19,22 +19,6 @@
/proc/arachnid_name()
return "[pick(GLOB.arachnid_first)] [pick(GLOB.arachnid_last)]"
-/proc/church_name()
- var/static/church_name
- if (church_name)
- return church_name
-
- var/name = ""
-
- name += pick("Holy", "United", "First", "Second", "Last")
-
- if (prob(20))
- name += " Space"
-
- name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses")
- name += " of [religion_name()]"
-
- return name
GLOBAL_VAR(command_name)
/proc/command_name()
@@ -52,17 +36,6 @@ GLOBAL_VAR(command_name)
return name
-/proc/religion_name()
- var/static/religion_name
- if (religion_name)
- return religion_name
-
- var/name = ""
-
- name += pick("bee", "science", "edu", "captain", "assistant", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob")
- name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity")
-
- return capitalize(name)
/proc/station_name()
if(!GLOB.station_name)
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index 0987e95dc9..87187a4c3d 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -78,21 +78,21 @@
//Turns a direction into text
/proc/dir2text(direction)
switch(direction)
- if(1)
+ if(NORTH)
return "north"
- if(2)
+ if(SOUTH)
return "south"
- if(4)
+ if(EAST)
return "east"
- if(8)
+ if(WEST)
return "west"
- if(5)
+ if(NORTHEAST)
return "northeast"
- if(6)
+ if(SOUTHEAST)
return "southeast"
- if(9)
+ if(NORTHWEST)
return "northwest"
- if(10)
+ if(SOUTHWEST)
return "southwest"
else
return
@@ -101,21 +101,21 @@
/proc/text2dir(direction)
switch(uppertext(direction))
if("NORTH")
- return 1
+ return NORTH
if("SOUTH")
- return 2
+ return SOUTH
if("EAST")
- return 4
+ return EAST
if("WEST")
- return 8
+ return WEST
if("NORTHEAST")
- return 5
+ return NORTHEAST
if("NORTHWEST")
- return 9
+ return NORTHWEST
if("SOUTHEAST")
- return 6
+ return SOUTHEAST
if("SOUTHWEST")
- return 10
+ return SOUTHWEST
else
return
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index d95c89d19e..9f89920b46 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1070,7 +1070,7 @@ B --><-- A
return closest_atom
-proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types())
+/proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types())
if (value == FALSE) //nothing should be calling us with a number, so this is safe
value = input("Enter type to find (blank for all, cancel to cancel)", "Search for type") as null|text
if (isnull(value))
@@ -1202,7 +1202,7 @@ GLOBAL_REAL_VAR(list/stack_trace_storage)
GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
//Version of view() which ignores darkness, because BYOND doesn't have it (I actually suggested it but it was tagged redundant, BUT HEARERS IS A T- /rant).
-/proc/dview(var/range = world.view, var/center, var/invis_flags = 0)
+/proc/dview(range = world.view, center, invis_flags = 0)
if(!center)
return
@@ -1222,6 +1222,10 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
var/ready_to_die = FALSE
/mob/dview/Initialize() //Properly prevents this mob from gaining huds or joining any global lists
+ SHOULD_CALL_PARENT(FALSE)
+ if(flags_1 & INITIALIZED_1)
+ stack_trace("Warning: [src]([type]) initialized multiple times!")
+ flags_1 |= INITIALIZED_1
return INITIALIZE_HINT_NORMAL
/mob/dview/Destroy(force = FALSE)
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index 0e0bd4ffaa..64b4129024 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -46,11 +46,11 @@
//Update this whenever you need to take advantage of more recent byond features
#define MIN_COMPILER_VERSION 513
-#define MIN_COMPILER_BUILD 1508
+#define MIN_COMPILER_BUILD 1514
#if DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD
//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 513.1508 or higher
+#error You need version 513.1514 or higher
#endif
//Additional code for the above flags.
@@ -62,10 +62,14 @@
#define FIND_REF_NO_CHECK_TICK
#endif
-#ifdef TRAVISBUILDING
+#ifdef CIBUILDING
#define UNIT_TESTS
#endif
-#ifdef TRAVISTESTING
+#ifdef CITESTING
#define TESTING
#endif
+
+// A reasonable number of maximum overlays an object needs
+// If you think you need more, rethink it
+#define MAX_ATOM_OVERLAYS 100
diff --git a/code/_globalvars/admin.dm b/code/_globalvars/admin.dm
new file mode 100644
index 0000000000..81037ff3dd
--- /dev/null
+++ b/code/_globalvars/admin.dm
@@ -0,0 +1,12 @@
+GLOBAL_LIST_EMPTY(stickybanadminexemptions) //stores a list of ckeys exempted from a stickyban (workaround for a bug)
+GLOBAL_LIST_EMPTY(stickybanadmintexts) //stores the entire stickyban list temporarily
+GLOBAL_VAR(stickbanadminexemptiontimerid) //stores the timerid of the callback that restores all stickybans after an admin joins
+
+// /proc/init_smites() //todo: add on the second wave
+// var/list/smites = list()
+// for (var/_smite_path in subtypesof(/datum/smite))
+// var/datum/smite/smite_path = _smite_path
+// smites[initial(smite_path.name)] = smite_path
+// return smites
+
+// GLOBAL_LIST_INIT_TYPED(smites, /datum/smite, init_smites())
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index e9f98f836e..78d802dbbf 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -8,6 +8,8 @@ GLOBAL_VAR(world_qdel_log)
GLOBAL_PROTECT(world_qdel_log)
GLOBAL_VAR(world_attack_log)
GLOBAL_PROTECT(world_attack_log)
+// GLOBAL_VAR(world_econ_log)
+// GLOBAL_PROTECT(world_econ_log)
GLOBAL_VAR(world_href_log)
GLOBAL_PROTECT(world_href_log)
GLOBAL_VAR(round_id)
@@ -26,22 +28,28 @@ GLOBAL_VAR(query_debug_log)
GLOBAL_PROTECT(query_debug_log)
GLOBAL_VAR(world_job_debug_log)
GLOBAL_PROTECT(world_job_debug_log)
+// GLOBAL_VAR(world_mecha_log)
+// GLOBAL_PROTECT(world_mecha_log)
GLOBAL_VAR(world_virus_log)
GLOBAL_PROTECT(world_virus_log)
GLOBAL_VAR(world_asset_log)
GLOBAL_PROTECT(world_asset_log)
+// GLOBAL_VAR(world_cloning_log)
+// GLOBAL_PROTECT(world_cloning_log)
GLOBAL_VAR(world_map_error_log)
GLOBAL_PROTECT(world_map_error_log)
GLOBAL_VAR(world_paper_log)
GLOBAL_PROTECT(world_paper_log)
-GLOBAL_VAR(subsystem_log)
-GLOBAL_PROTECT(subsystem_log)
-GLOBAL_VAR(reagent_log)
-GLOBAL_PROTECT(reagent_log)
-GLOBAL_VAR(world_crafting_log)
-GLOBAL_PROTECT(world_crafting_log)
-GLOBAL_VAR(click_log)
-GLOBAL_PROTECT(click_log)
+GLOBAL_VAR(tgui_log)
+GLOBAL_PROTECT(tgui_log)
+GLOBAL_VAR(world_shuttle_log)
+GLOBAL_PROTECT(world_shuttle_log)
+
+GLOBAL_VAR(perf_log)
+GLOBAL_PROTECT(perf_log)
+
+// GLOBAL_VAR(demo_log)
+// GLOBAL_PROTECT(demo_log)
GLOBAL_LIST_EMPTY(bombers)
GLOBAL_PROTECT(bombers)
@@ -51,10 +59,7 @@ GLOBAL_LIST_EMPTY(lastsignalers) //keeps last 100 signals here in format: "[src]
GLOBAL_PROTECT(lastsignalers)
GLOBAL_LIST_EMPTY(lawchanges) //Stores who uploaded laws to which silicon-based lifeform, and what the law was
GLOBAL_PROTECT(lawchanges)
-GLOBAL_VAR(tgui_log)
-GLOBAL_PROTECT(tgui_log)
-GLOBAL_VAR(world_shuttle_log)
-GLOBAL_PROTECT(world_shuttle_log)
+
GLOBAL_LIST_EMPTY(combatlog)
GLOBAL_PROTECT(combatlog)
GLOBAL_LIST_EMPTY(IClog)
@@ -75,3 +80,13 @@ GLOBAL_PROTECT(picture_logging_id)
GLOBAL_VAR(picture_logging_prefix)
GLOBAL_PROTECT(picture_logging_prefix)
/////
+
+//// cit logging
+GLOBAL_VAR(subsystem_log)
+GLOBAL_PROTECT(subsystem_log)
+GLOBAL_VAR(reagent_log)
+GLOBAL_PROTECT(reagent_log)
+GLOBAL_VAR(world_crafting_log)
+GLOBAL_PROTECT(world_crafting_log)
+GLOBAL_VAR(click_log)
+GLOBAL_PROTECT(click_log)
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 4e593ba904..f376ba50d7 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -130,7 +130,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/obj/item = list(
"TRAIT_NODROP" = TRAIT_NODROP,
- "TRAIT_NO_TELEPORT" = TRAIT_NO_TELEPORT
+ "TRAIT_NO_TELEPORT" = TRAIT_NO_TELEPORT,
+ "TRAIT_SPOOKY_THROW" = TRAIT_SPOOKY_THROW
)
))
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 5bd9632355..81d8593d4e 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -220,7 +220,7 @@
/obj/screen/alert/shiver
name = "Shivering"
- desc = "You're shivering! Get somewhere warmer and take off any insulating clothing like a space suit."
+ desc = "You're shivering! Get somewhere warmer and take off any insulating clothing like a space suit."
/obj/screen/alert/lowpressure
name = "Low Pressure"
@@ -306,6 +306,39 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
if(CHECK_MOBILITY(L, MOBILITY_MOVE))
return L.resist_fire() //I just want to start a flame in your hearrrrrrtttttt.
+/obj/screen/alert/give // information set when the give alert is made
+ icon_state = "default"
+ var/mob/living/carbon/giver
+ var/obj/item/receiving
+
+/**
+ * Handles assigning most of the variables for the alert that pops up when an item is offered
+ *
+ * Handles setting the name, description and icon of the alert and tracking the person giving
+ * and the item being offered, also registers a signal that removes the alert from anyone who moves away from the giver
+ * Arguments:
+ * * taker - The person receiving the alert
+ * * giver - The person giving the alert and item
+ * * receiving - The item being given by the giver
+ */
+/obj/screen/alert/give/proc/setup(mob/living/carbon/taker, mob/living/carbon/giver, obj/item/receiving)
+ name = "[giver] is offering [receiving]"
+ desc = "[giver] is offering [receiving]. Click this alert to take it."
+ icon_state = "template"
+ cut_overlays()
+ add_overlay(receiving)
+ src.receiving = receiving
+ src.giver = giver
+ RegisterSignal(taker, COMSIG_MOVABLE_MOVED, .proc/removeAlert)
+
+/obj/screen/alert/give/proc/removeAlert()
+ to_chat(usr, "You moved out of range of [giver]!")
+ usr.clear_alert("[giver]")
+
+/obj/screen/alert/give/Click(location, control, params)
+ . = ..()
+ var/mob/living/carbon/C = usr
+ C.take(giver, receiving)
//ALIENS
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm
index d82f3e7cf5..076c949f8e 100644
--- a/code/_onclick/hud/fullscreen.dm
+++ b/code/_onclick/hud/fullscreen.dm
@@ -149,6 +149,15 @@
/obj/screen/fullscreen/color_vision/blue
color = "#0000ff"
+/obj/screen/fullscreen/cinematic_backdrop
+ icon = 'icons/mob/screen_gen.dmi'
+ screen_loc = "WEST,SOUTH to EAST,NORTH"
+ icon_state = "flash"
+ plane = SPLASHSCREEN_PLANE
+ layer = SPLASHSCREEN_LAYER - 1
+ color = "#000000"
+ show_when_dead = TRUE
+
/obj/screen/fullscreen/lighting_backdrop
icon = 'icons/mob/screen_gen.dmi'
icon_state = "flash"
diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm
index 9050bcb5f0..5d48f430d7 100755
--- a/code/_onclick/hud/parallax.dm
+++ b/code/_onclick/hud/parallax.dm
@@ -10,6 +10,8 @@
C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_1(null, C.view)
C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_2(null, C.view)
C.parallax_layers_cached += new /obj/screen/parallax_layer/planet(null, C.view)
+ if(SSparallax.random_layer)
+ C.parallax_layers_cached += new SSparallax.random_layer
C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_3(null, C.view)
C.parallax_layers = C.parallax_layers_cached.Copy()
@@ -52,12 +54,12 @@
switch(C.prefs.parallax)
if (PARALLAX_INSANE)
C.parallax_throttle = FALSE
- C.parallax_layers_max = 4
+ C.parallax_layers_max = 5
return TRUE
if (PARALLAX_MED)
C.parallax_throttle = PARALLAX_DELAY_MED
- C.parallax_layers_max = 2
+ C.parallax_layers_max = 3
return TRUE
if (PARALLAX_LOW)
@@ -68,8 +70,9 @@
if (PARALLAX_DISABLE)
return FALSE
+ //This is high parallax.
C.parallax_throttle = PARALLAX_DELAY_DEFAULT
- C.parallax_layers_max = 3
+ C.parallax_layers_max = 4
return TRUE
/datum/hud/proc/update_parallax_pref(mob/viewmob)
@@ -219,15 +222,14 @@
L.screen_loc = "CENTER-7:[round(L.offset_x,1)],CENTER-7:[round(L.offset_y,1)]"
/atom/movable/proc/update_parallax_contents()
- set waitfor = FALSE
if(length(client_mobs_in_contents))
for(var/thing in client_mobs_in_contents)
var/mob/M = thing
- if(M && M.client && M.hud_used && length(M.client.parallax_layers))
+ if(M?.client && M.hud_used && length(M.client.parallax_layers))
M.hud_used.update_parallax()
/mob/proc/update_parallax_teleport() //used for arrivals shuttle
- if(client && client.eye && hud_used && length(client.parallax_layers))
+ if(client?.eye && hud_used && length(client.parallax_layers))
var/area/areaobj = get_area(client.eye)
hud_used.set_parallax_movedir(areaobj.parallax_movedir, TRUE)
@@ -287,6 +289,21 @@
speed = 1.4
layer = 3
+/obj/screen/parallax_layer/random
+ blend_mode = BLEND_OVERLAY
+ speed = 3
+ layer = 3
+
+/obj/screen/parallax_layer/random/space_gas
+ icon_state = "space_gas"
+
+/obj/screen/parallax_layer/random/space_gas/Initialize(mapload, view)
+ . = ..()
+ src.add_atom_colour(SSparallax.random_parallax_color, ADMIN_COLOUR_PRIORITY)
+
+/obj/screen/parallax_layer/random/asteroids
+ icon_state = "asteroids"
+
/obj/screen/parallax_layer/planet
icon_state = "planet"
blend_mode = BLEND_OVERLAY
@@ -295,11 +312,11 @@
layer = 30
/obj/screen/parallax_layer/planet/update_status(mob/M)
- var/turf/T = get_turf(M)
- if(is_station_level(T.z))
- invisibility = 0
- else
- invisibility = INVISIBILITY_ABSTRACT
+ var/client/C = M.client
+ var/turf/posobj = get_turf(C.eye)
+ if(!posobj)
+ return
+ invisibility = is_station_level(posobj.z) ? 0 : INVISIBILITY_ABSTRACT
/obj/screen/parallax_layer/planet/update_o()
- return //Shit wont move
+ return //Shit won't move
diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm
index 7a8b0a1121..f5b8991e20 100644
--- a/code/_onclick/hud/plane_master.dm
+++ b/code/_onclick/hud/plane_master.dm
@@ -28,8 +28,6 @@
. = ..()
filters += filter(type="alpha", render_source=FIELD_OF_VISION_RENDER_TARGET, flags=MASK_INVERSE)
-/obj/screen/plane_master/openspace/backdrop(mob/mymob)
- filters = list()
filters += filter(type = "drop_shadow", color = "#04080FAA", size = -10)
filters += filter(type = "drop_shadow", color = "#04080FAA", size = -15)
filters += filter(type = "drop_shadow", color = "#04080FAA", size = -20)
@@ -93,13 +91,6 @@
else
remove_filter("ambient_occlusion")
-//Reserved to chat messages, so they are still displayed above the field of vision masking.
-/obj/screen/plane_master/chat_messages
- name = "chat messages plane master"
- plane = CHAT_PLANE
- appearance_flags = PLANE_MASTER
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
-
///Contains all shadow cone masks, whose image overrides are displayed only to their respective owners.
/obj/screen/plane_master/field_of_vision
name = "field of vision mask plane master"
@@ -135,10 +126,14 @@
blend_mode = BLEND_MULTIPLY
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+/obj/screen/plane_master/lighting/backdrop(mob/mymob)
+ mymob.overlay_fullscreen("lighting_backdrop_lit", /obj/screen/fullscreen/lighting_backdrop/lit)
+ mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit)
+
/obj/screen/plane_master/lighting/Initialize()
. = ..()
- filters += filter(type="alpha", render_source=EMISSIVE_RENDER_TARGET, flags=MASK_INVERSE)
- filters += filter(type="alpha", render_source=EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags=MASK_INVERSE)
+ filters += filter(type="alpha", render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE)
+ filters += filter(type="alpha", render_source = EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags = MASK_INVERSE)
/**
* Things placed on this mask the lighting plane. Doesn't render directly.
@@ -186,7 +181,6 @@
render_target = EMISSIVE_BLOCKER_RENDER_TARGET
///Contains space parallax
-
/obj/screen/plane_master/parallax
name = "parallax plane master"
plane = PLANE_SPACE_PARALLAX
@@ -197,12 +191,16 @@
name = "parallax whitifier plane master"
plane = PLANE_SPACE
-/obj/screen/plane_master/lighting/backdrop(mob/mymob)
- mymob.overlay_fullscreen("lighting_backdrop_lit", /obj/screen/fullscreen/lighting_backdrop/lit)
- mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit)
-
/obj/screen/plane_master/camera_static
name = "camera static plane master"
plane = CAMERA_STATIC_PLANE
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
+
+
+//Reserved to chat messages, so they are still displayed above the field of vision masking.
+/obj/screen/plane_master/chat_messages
+ name = "runechat plane master"
+ plane = CHAT_PLANE
+ appearance_flags = PLANE_MASTER
+ blend_mode = BLEND_OVERLAY
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 2ed8c81ba2..53915ff42b 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -12,9 +12,14 @@
layer = HUD_LAYER
plane = HUD_PLANE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ animate_movement = SLIDE_STEPS
+ speech_span = SPAN_ROBOT
+ vis_flags = VIS_INHERIT_PLANE
appearance_flags = APPEARANCE_UI
- var/obj/master = null //A reference to the object in the slot. Grabs or items, generally.
- var/datum/hud/hud = null // A reference to the owner HUD, if any.
+ /// A reference to the object in the slot. Grabs or items, generally.
+ var/obj/master = null
+ /// A reference to the owner HUD, if any.
+ var/datum/hud/hud = null
/**
* Map name assigned to this object.
* Automatically set by /client/proc/add_obj_to_map.
@@ -60,7 +65,17 @@
name = "swap hand"
/obj/screen/swap_hand/Click()
- usr.swap_hand()
+ // At this point in client Click() code we have passed the 1/10 sec check and little else
+ // We don't even know if it's a middle click
+ // if(world.time <= usr.next_move)
+ // return 1
+
+ if(usr.incapacitated())
+ return 1
+
+ if(ismob(usr))
+ var/mob/M = usr
+ M.swap_hand()
return 1
/obj/screen/craft
@@ -96,17 +111,27 @@
H.open_language_menu(usr)
/obj/screen/inventory
- var/slot_id // The indentifier for the slot. It has nothing to do with ID cards.
- var/icon_empty // Icon when empty. For now used only by humans.
- var/icon_full // Icon when contains an item. For now used only by humans.
+ /// The identifier for the slot. It has nothing to do with ID cards.
+ var/slot_id
+ /// Icon when empty. For now used only by humans.
+ var/icon_empty
+ /// Icon when contains an item. For now used only by humans.
+ var/icon_full
+ /// The overlay when hovering over with an item in your hand
var/list/object_overlays = list()
layer = HUD_LAYER
plane = HUD_PLANE
/obj/screen/inventory/Click(location, control, params)
- if(hud?.mymob && (hud.mymob != usr))
- return
- // just redirect clicks
+ // At this point in client Click() code we have passed the 1/10 sec check and little else
+ // We don't even know if it's a middle click
+ // if(world.time <= usr.next_move)
+ // return TRUE
+
+ if(usr.incapacitated()) // ignore_stasis = TRUE
+ return TRUE
+ if(ismecha(usr.loc)) // stops inventory actions in a mech
+ return TRUE
if(hud?.mymob && slot_id)
var/obj/item/inv_item = hud.mymob.get_item_by_slot(slot_id)
@@ -150,12 +175,13 @@
var/image/item_overlay = image(holding)
item_overlay.alpha = 92
- if(!user.can_equip(holding, slot_id, TRUE, TRUE, TRUE))
+ if(!user.can_equip(holding, slot_id, TRUE))
item_overlay.color = "#FF0000"
else
item_overlay.color = "#00ff00"
- object_overlays += item_overlay
+ cut_overlay(object_overlays)
+ // object_overlay = item_overlay
add_overlay(object_overlays)
/obj/screen/inventory/hand
@@ -187,10 +213,17 @@
/obj/screen/inventory/hand/Click(location, control, params)
- if(hud?.mymob && (hud.mymob != usr))
- return
- var/mob/user = hud.mymob
- // just redirect clicks
+ // At this point in client Click() code we have passed the 1/10 sec check and little else
+ // We don't even know if it's a middle click
+ var/mob/user = hud?.mymob
+ if(usr != user)
+ return TRUE
+ // if(world.time <= user.next_move)
+ // return TRUE
+ if(user.incapacitated())
+ return TRUE
+ if (ismecha(user.loc)) // stops inventory actions in a mech
+ return TRUE
if(user.active_hand_index == held_index)
var/obj/item/I = user.get_active_held_item()
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index cd1ee56848..7a614da07b 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -85,7 +85,7 @@
if(force && damtype != STAMINA && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "You don't want to harm other living beings!")
return
-
+
if(!UseStaminaBufferStandard(user, STAM_COST_ATTACK_MOB_MULT, null, TRUE))
return DISCARD_LAST_ACTION
diff --git a/code/controllers/admin.dm b/code/controllers/admin.dm
index 3782d8be94..19fef28597 100644
--- a/code/controllers/admin.dm
+++ b/code/controllers/admin.dm
@@ -3,7 +3,7 @@
name = "Initializing..."
var/target
-INITIALIZE_IMMEDIATE(/obj/effect/statclick) //it's new, but rebranded.
+INITIALIZE_IMMEDIATE(/obj/effect/statclick)
/obj/effect/statclick/Initialize(mapload, text, target) //Don't port this to Initialize it's too critical
. = ..()
@@ -33,14 +33,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick) //it's new, but rebranded.
usr.client.debug_variables(target)
message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
-/obj/effect/statclick/misc_subsystems/Click()
- if(!usr.client.holder)
- return
- var/subsystem = input(usr, "Debug which subsystem?", "Debug nonprocessing subsystem") as null|anything in (Master.subsystems - Master.statworthy_subsystems)
- if(!subsystem)
- return
- usr.client.debug_variables(subsystem)
- message_admins("Admin [key_name_admin(usr)] is debugging the [subsystem] subsystem.")
// Debug verbs.
/client/proc/restart_controller(controller in list("Master", "Failsafe"))
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index fdce9a1287..a15056e442 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -1,7 +1,7 @@
/**
- * Failsafe
- *
- * Pretty much pokes the MC to make sure it's still alive.
+ * Failsafe
+ *
+ * Pretty much pokes the MC to make sure it's still alive.
**/
GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 1b5c7e5e83..59ac68960c 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -1,10 +1,10 @@
- /**
- * StonedMC
- *
- * Designed to properly split up a given tick among subsystems
- * Note: if you read parts of this code and think "why is it doing it that way"
- * Odds are, there is a reason
- *
+/**
+ * StonedMC
+ *
+ * Designed to properly split up a given tick among subsystems
+ * Note: if you read parts of this code and think "why is it doing it that way"
+ * Odds are, there is a reason
+ *
**/
//This is the ABSOLUTE ONLY THING that should init globally like this
@@ -28,8 +28,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// List of subsystems to process().
var/list/subsystems
- /// List of subsystems to include in the MC stat panel.
- var/list/statworthy_subsystems
// Vars for keeping track of tick drift.
var/init_timeofday
@@ -41,7 +39,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
///Only run ticker subsystems for the next n ticks.
var/skip_ticks = 0
- var/make_runtime = 0
+ var/make_runtime = FALSE
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
@@ -67,9 +65,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING
- /// Statclick for misc subsystems
- var/obj/effect/statclick/misc_subsystems/misc_statclick
-
/datum/controller/master/New()
if(!config)
config = new
@@ -96,11 +91,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
_subsystems += new I
Master = src
- // We want to see all subsystems during init.
- statworthy_subsystems = subsystems.Copy()
-
- misc_statclick = new(null, "Debug")
-
if(!GLOB)
new /datum/controller/global_vars
@@ -217,7 +207,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// Sort subsystems by display setting for easy access.
sortTim(subsystems, /proc/cmp_subsystem_display)
// Set world options.
- world.fps = CONFIG_GET(number/fps)
+ world.change_fps(CONFIG_GET(number/fps))
var/initialized_tod = REALTIMEOFDAY
if(tgs_prime)
@@ -271,14 +261,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/list/tickersubsystems = list()
var/list/runlevel_sorted_subsystems = list(list()) //ensure we always have at least one runlevel
var/timer = world.time
- statworthy_subsystems = list()
for (var/thing in subsystems)
var/datum/controller/subsystem/SS = thing
if (SS.flags & SS_NO_FIRE)
- if(SS.flags & SS_ALWAYS_SHOW_STAT)
- statworthy_subsystems += SS
continue
- statworthy_subsystems += SS
SS.queued_time = 0
SS.queue_next = null
SS.queue_prev = null
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index e49da32557..12798f3863 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -23,7 +23,7 @@
var/priority = FIRE_PRIORITY_DEFAULT
/// [Subsystem Flags][SS_NO_INIT] to control binary behavior. Flags must be set at compile time or before preinit finishes to take full effect. (You can also restart the mc to force them to process again)
- var/flags = 0
+ var/flags = NONE
/// This var is set to TRUE after the subsystem has been initialized.
var/initialized = FALSE
@@ -114,7 +114,7 @@
//previously, this would have been named 'process()' but that name is used everywhere for different things!
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
//Sleeping in here prevents future fires until returned.
-/datum/controller/subsystem/proc/fire(resumed = 0)
+/datum/controller/subsystem/proc/fire(resumed = FALSE)
flags |= SS_NO_FIRE
CRASH("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.")
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index f777c967ae..3c58c90452 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -241,7 +241,8 @@ SUBSYSTEM_DEF(air)
return
/datum/controller/subsystem/air/proc/process_turf_equalize(resumed = 0)
- return process_turf_equalize_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag)
+ if(process_turf_equalize_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag))
+ pause()
/*
//cache for sanic speed
var/fire_count = times_fired
@@ -260,7 +261,8 @@ SUBSYSTEM_DEF(air)
*/
/datum/controller/subsystem/air/proc/process_active_turfs(resumed = 0)
- return process_active_turfs_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag)
+ if(process_active_turfs_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag))
+ pause()
/*
//cache for sanic speed
var/fire_count = times_fired
@@ -278,7 +280,8 @@ SUBSYSTEM_DEF(air)
*/
/datum/controller/subsystem/air/proc/process_excited_groups(resumed = 0)
- return process_excited_groups_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag)
+ if(process_excited_groups_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag))
+ pause()
/*
if (!resumed)
src.currentrun = excited_groups.Copy()
diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm
index 4f02d32ad0..4b43f98290 100644
--- a/code/controllers/subsystem/assets.dm
+++ b/code/controllers/subsystem/assets.dm
@@ -11,7 +11,7 @@ SUBSYSTEM_DEF(assets)
switch (CONFIG_GET(string/asset_transport))
if ("webroot")
newtransporttype = /datum/asset_transport/webroot
-
+
if (newtransporttype == transport.type)
return
diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm
index b9a4785f49..02aad6dec3 100644
--- a/code/controllers/subsystem/atoms.dm
+++ b/code/controllers/subsystem/atoms.dm
@@ -10,33 +10,37 @@ SUBSYSTEM_DEF(atoms)
var/old_initialized
- var/list/late_loaders
+ var/list/late_loaders = list()
var/list/BadInitializeCalls = list()
+ initialized = INITIALIZATION_INSSATOMS
+
/datum/controller/subsystem/atoms/Initialize(timeofday)
GLOB.fire_overlay.appearance_flags = RESET_COLOR
- setupGenetics()
+ setupGenetics() //to set the mutations' sequence
+
initialized = INITIALIZATION_INNEW_MAPLOAD
InitializeAtoms()
+ initialized = INITIALIZATION_INNEW_REGULAR
return ..()
/datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms)
if(initialized == INITIALIZATION_INSSATOMS)
return
+ old_initialized = initialized
initialized = INITIALIZATION_INNEW_MAPLOAD
- LAZYINITLIST(late_loaders)
-
var/count
var/list/mapload_arg = list(TRUE)
+
if(atoms)
count = atoms.len
- for(var/I in atoms)
- var/atom/A = I
+ for(var/I in 1 to count)
+ var/atom/A = atoms[I]
if(!(A.flags_1 & INITIALIZED_1))
- InitAtom(I, mapload_arg)
+ InitAtom(A, mapload_arg)
CHECK_TICK
else
count = 0
@@ -49,15 +53,16 @@ SUBSYSTEM_DEF(atoms)
testing("Initialized [count] atoms")
pass(count)
- initialized = INITIALIZATION_INNEW_REGULAR
+ initialized = old_initialized
if(late_loaders.len)
- for(var/I in late_loaders)
- var/atom/A = I
+ for(var/I in 1 to late_loaders.len)
+ var/atom/A = late_loaders[I]
A.LateInitialize()
testing("Late initialized [late_loaders.len] atoms")
late_loaders.Cut()
+/// Init this specific atom
/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments)
var/the_type = A.type
if(QDELING(A))
@@ -150,8 +155,3 @@ SUBSYSTEM_DEF(atoms)
var/initlog = InitLog()
if(initlog)
text2file(initlog, "[GLOB.log_directory]/initialize.log")
-
-#undef BAD_INIT_QDEL_BEFORE
-#undef BAD_INIT_DIDNT_INIT
-#undef BAD_INIT_SLEPT
-#undef BAD_INIT_NO_HINT
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index dc1f246e3d..bacccefc61 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -14,12 +14,14 @@ SUBSYSTEM_DEF(blackbox)
"explosion" = 2,
"time_dilation_current" = 3,
"science_techweb_unlock" = 2,
- "round_end_stats" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
+ "round_end_stats" = 2,
+ "testmerged_prs" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
/datum/controller/subsystem/blackbox/Initialize()
triggertime = world.time
record_feedback("amount", "random_seed", Master.random_seed)
record_feedback("amount", "dm_version", DM_VERSION)
+ record_feedback("amount", "dm_build", DM_BUILD)
record_feedback("amount", "byond_version", world.byond_version)
record_feedback("amount", "byond_build", world.byond_build)
. = ..()
@@ -39,10 +41,7 @@ SUBSYSTEM_DEF(blackbox)
if(!SSdbcore.Connect())
return
- var/playercount = 0
- for(var/mob/M in GLOB.player_list)
- if(M.client)
- playercount += 1
+ var/playercount = LAZYLEN(GLOB.player_list)
var/admincount = GLOB.admins.len
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port, round_id) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]')")
query_record_playercount.Execute()
@@ -88,18 +87,24 @@ SUBSYSTEM_DEF(blackbox)
if (!SSdbcore.Connect())
return
+ // var/list/special_columns = list(
+ // "datetime" = "NOW()"
+ // )
var/list/sqlrowlist = list()
-
for (var/datum/feedback_variable/FV in feedback)
- var/sqlversion = 1
- if(FV.key in versions)
- sqlversion = versions[FV.key]
- sqlrowlist += list(list("datetime" = "Now()", "round_id" = GLOB.round_id, "key_name" = "'[sanitizeSQL(FV.key)]'", "key_type" = "'[FV.key_type]'", "version" = "[sqlversion]", "json" = "'[sanitizeSQL(json_encode(FV.json))]'"))
+ sqlrowlist += list(list(
+ "datetime" = "Now()", //legacy
+ "round_id" = GLOB.round_id,
+ "key_name" = sanitizeSQL(FV.key),
+ "key_type" = FV.key_type,
+ "version" = versions[FV.key] || 1,
+ "json" = sanitizeSQL(json_encode(FV.json))
+ ))
if (!length(sqlrowlist))
return
- SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
+ SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)//, special_columns = special_columns)
/datum/controller/subsystem/blackbox/proc/Seal()
if(sealed)
@@ -169,7 +174,7 @@ feedback data can be recorded in 5 formats:
"tally"
used to track the number of occurances of multiple related values i.e. how many times each type of gun is fired
further calls to the same key will:
- add or subtract from the saved value of the data key if it already exists
+ add or subtract from the saved value of the data key if it already exists
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("tally", "example", 1, "sample data")
SSblackbox.record_feedback("tally", "example", 4, "sample data")
@@ -181,7 +186,7 @@ feedback data can be recorded in 5 formats:
the final element in the data list is used as the tracking key, all prior elements are used for nesting
all data list elements must be strings
further calls to the same key will:
- add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
+ add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
SSblackbox.record_feedback("nested tally", "example", 2, list("fruit", "orange", "orange"))
@@ -270,6 +275,18 @@ Versioning
/datum/feedback_variable/New(new_key, new_key_type)
key = new_key
key_type = new_key_type
+/*
+/datum/controller/subsystem/blackbox/proc/LogAhelp(ticket, action, message, recipient, sender)
+ if(!SSdbcore.Connect())
+ return
+
+ var/datum/db_query/query_log_ahelp = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("ticket")] (ticket, action, message, recipient, sender, server_ip, server_port, round_id, timestamp)
+ VALUES (:ticket, :action, :message, :recipient, :sender, INET_ATON(:server_ip), :server_port, :round_id, :time)
+ "}, list("ticket" = ticket, "action" = action, "message" = message, "recipient" = recipient, "sender" = sender, "server_ip" = world.internet_address || "0", "server_port" = world.port, "round_id" = GLOB.round_id, "time" = SQLtime()))
+ query_log_ahelp.Execute()
+ qdel(query_log_ahelp)
+*/
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
set waitfor = FALSE
diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm
index f2e9da704f..0e4f8ecad2 100644
--- a/code/controllers/subsystem/chat.dm
+++ b/code/controllers/subsystem/chat.dm
@@ -1,4 +1,4 @@
-/**
+/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm
index 4eff4fbf1d..c779c9f26d 100644
--- a/code/controllers/subsystem/dbcore.dm
+++ b/code/controllers/subsystem/dbcore.dm
@@ -177,6 +177,25 @@ SUBSYSTEM_DEF(dbcore)
return FALSE
return new /datum/DBQuery(sql_query, connection)
+/datum/controller/subsystem/dbcore/proc/QuerySelect(list/querys, warn = FALSE, qdel = FALSE)
+ if (!islist(querys))
+ if (!istype(querys, /datum/DBQuery))
+ CRASH("Invalid query passed to QuerySelect: [querys]")
+ querys = list(querys)
+
+ for (var/thing in querys)
+ var/datum/DBQuery/query = thing
+ if (warn)
+ INVOKE_ASYNC(query, /datum/DBQuery.proc/warn_execute)
+ else
+ INVOKE_ASYNC(query, /datum/DBQuery.proc/Execute)
+
+ for (var/thing in querys)
+ var/datum/DBQuery/query = thing
+ UNTIL(!query.in_progress)
+ if (qdel)
+ qdel(query)
+
/*
Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
Rows missing columns present in other rows will resolve to SQL NULL
@@ -361,5 +380,5 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
//strip sensitive stuff
if(findtext(message, ": CreateConnection("))
message = "CreateConnection CENSORED"
-
+
log_sql("BSQL_DEBUG: [message]")
diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm
index 9bc8a631c1..3660770596 100644
--- a/code/controllers/subsystem/events.dm
+++ b/code/controllers/subsystem/events.dm
@@ -25,7 +25,7 @@ SUBSYSTEM_DEF(events)
return ..()
-/datum/controller/subsystem/events/fire(resumed = 0)
+/datum/controller/subsystem/events/fire(resumed = FALSE)
if(!resumed)
checkEvent() //only check these if we aren't resuming a paused fire
src.currentrun = running.Copy()
@@ -37,7 +37,7 @@ SUBSYSTEM_DEF(events)
var/datum/thing = currentrun[currentrun.len]
currentrun.len--
if(thing)
- thing.process()
+ thing.process(wait * 0.1)
else
running.Remove(thing)
if (MC_TICK_CHECK)
@@ -91,13 +91,13 @@ SUBSYSTEM_DEF(events)
if(. == EVENT_CANT_RUN)//we couldn't run this event for some reason, set its max_occurrences to 0
E.max_occurrences = 0
else if(. == EVENT_READY)
- E.random = TRUE
- E.runEvent(TRUE)
+ E.runEvent(random = TRUE)
//allows a client to trigger an event
//aka Badmin Central
// > Not in modules/admin
// REEEEEEEEE
+// Why the heck is this here! Took me so damn long to find!
/client/proc/forceEvent()
set name = "Trigger Event"
set category = "Admin.Events"
diff --git a/code/controllers/subsystem/fire_burning.dm b/code/controllers/subsystem/fire_burning.dm
index 3251285ade..f81c23d186 100644
--- a/code/controllers/subsystem/fire_burning.dm
+++ b/code/controllers/subsystem/fire_burning.dm
@@ -18,6 +18,7 @@ SUBSYSTEM_DEF(fire_burning)
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
+ var/delta_time = wait * 0.1
while(currentrun.len)
var/obj/O = currentrun[currentrun.len]
@@ -28,10 +29,12 @@ SUBSYSTEM_DEF(fire_burning)
return
continue
- if(O.resistance_flags & ON_FIRE)
- O.take_damage(20, BURN, "fire", 0)
- else
- processing -= O
+
+ if(O.resistance_flags & ON_FIRE) //in case an object is extinguished while still in currentrun
+ if(!(O.resistance_flags & FIRE_PROOF))
+ O.take_damage(10 * delta_time, BURN, "fire", 0)
+ else
+ O.extinguish()
if (MC_TICK_CHECK)
return
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index da3bb24a2f..2d2fac1d13 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -1,3 +1,26 @@
+/*!
+## Debugging GC issues
+
+In order to debug `qdel()` failures, there are several tools available.
+To enable these tools, define `TESTING` in [_compile_options.dm](https://github.com/tgstation/-tg-station/blob/master/code/_compile_options.dm).
+
+First is a verb called "Find References", which lists **every** refererence to an object in the world. This allows you to track down any indirect or obfuscated references that you might have missed.
+
+Complementing this is another verb, "qdel() then Find References".
+This does exactly what you'd expect; it calls `qdel()` on the object and then it finds all references remaining.
+This is great, because it means that `Destroy()` will have been called before it starts to find references,
+so the only references you'll find will be the ones preventing the object from `qdel()`ing gracefully.
+
+If you have a datum or something you are not destroying directly (say via the singulo),
+the next tool is `QDEL_HINT_FINDREFERENCE`. You can return this in `Destroy()` (where you would normally `return ..()`),
+to print a list of references once it enters the GC queue.
+
+Finally is a verb, "Show qdel() Log", which shows the deletion log that the garbage subsystem keeps. This is helpful if you are having race conditions or need to review the order of deletions.
+
+Note that for any of these tools to work `TESTING` must be defined.
+By using these methods of finding references, you can make your life far, far easier when dealing with `qdel()` failures.
+*/
+
SUBSYSTEM_DEF(garbage)
name = "Garbage"
priority = FIRE_PRIORITY_GARBAGE
@@ -6,7 +29,7 @@ SUBSYSTEM_DEF(garbage)
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
init_order = INIT_ORDER_GARBAGE
- var/list/collection_timeout = list(15 SECONDS, 30 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
+ var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
//Stat tracking
var/delslasttick = 0 // number of del()'s we've done this tick
@@ -24,10 +47,8 @@ SUBSYSTEM_DEF(garbage)
//Queue
var/list/queues
-
#ifdef LEGACY_REFERENCE_TRACKING
var/list/reference_find_on_fail = list()
- var/list/reference_find_on_fail_types = list()
#endif
@@ -99,6 +120,9 @@ SUBSYSTEM_DEF(garbage)
state = SS_RUNNING
break
+
+
+
/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK)
if (level == GC_QUEUE_CHECK)
delslasttick = 0
@@ -139,7 +163,7 @@ SUBSYSTEM_DEF(garbage)
++totalgcs
pass_counts[level]++
#ifdef LEGACY_REFERENCE_TRACKING
- reference_find_on_fail -= refID //It's deleted we don't care anymore.
+ reference_find_on_fail -= refID //It's deleted we don't care anymore.
#endif
if (MC_TICK_CHECK)
return
@@ -153,10 +177,10 @@ SUBSYSTEM_DEF(garbage)
D.find_references()
#elif defined(LEGACY_REFERENCE_TRACKING)
if(reference_find_on_fail[refID])
- D.find_references()
+ D.find_references_legacy()
#ifdef GC_FAILURE_HARD_LOOKUP
else
- D.find_references()
+ D.find_references_legacy()
#endif
reference_find_on_fail -= refID
#endif
@@ -190,24 +214,6 @@ SUBSYSTEM_DEF(garbage)
queue.Cut(1,count+1)
count = 0
-/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK)
- if (isnull(D))
- return
- if (level > GC_QUEUE_COUNT)
- HardDelete(D)
- return
- var/gctime = world.time
- var/refid = "\ref[D]"
-
-#ifdef LEGACY_REFERENCE_TRACKING
- if(reference_find_on_fail_types[D.type])
- reference_find_on_fail["\ref[D]"] = TRUE
-#endif
-
- D.gc_destroyed = gctime
- var/list/queue = queues[level]
- queue[++queue.len] = list(gctime, refid) // not += for byond reasons
-
#ifdef LEGACY_REFERENCE_TRACKING
/datum/controller/subsystem/garbage/proc/add_type_to_findref(type)
if(!ispath(type))
@@ -223,6 +229,24 @@ SUBSYSTEM_DEF(garbage)
reference_find_on_fail_types = list()
#endif
+/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK)
+ if (isnull(D))
+ return
+ if (level > GC_QUEUE_COUNT)
+ HardDelete(D)
+ return
+ var/gctime = world.time
+ var/refid = "\ref[D]"
+
+#ifdef LEGACY_REFERENCE_TRACKING
+ if(reference_find_on_fail_types[D.type])
+ SSgarbage.reference_find_on_fail[REF(D)] = TRUE
+#endif
+
+ D.gc_destroyed = gctime
+ var/list/queue = queues[level]
+ queue[++queue.len] = list(gctime, refid) // not += for byond reasons
+
//this is mainly to separate things profile wise.
/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
var/time = world.timeofday
@@ -275,8 +299,10 @@ SUBSYSTEM_DEF(garbage)
/datum/qdel_item/New(mytype)
name = "[mytype]"
-// Should be treated as a replacement for the 'del' keyword.
-// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
+
+/// Should be treated as a replacement for the 'del' keyword.
+///
+/// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
/proc/qdel(datum/D, force=FALSE, ...)
if(!istype(D))
del(D)
@@ -331,9 +357,10 @@ SUBSYSTEM_DEF(garbage)
#ifdef LEGACY_REFERENCE_TRACKING
if (QDEL_HINT_FINDREFERENCE) //qdel will, if LEGACY_REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion.
SSgarbage.Queue(D)
+ D.find_references_legacy()
if (QDEL_HINT_IFFAIL_FINDREFERENCE)
SSgarbage.Queue(D)
- SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE
+ SSgarbage.reference_find_on_fail[REF(D)] = TRUE
#endif
else
#ifdef TESTING
diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm
index 03b7931d82..ccdaa555a2 100644
--- a/code/controllers/subsystem/idlenpcpool.dm
+++ b/code/controllers/subsystem/idlenpcpool.dm
@@ -33,8 +33,9 @@ SUBSYSTEM_DEF(idlenpcpool)
while(currentrun.len)
var/mob/living/simple_animal/SA = currentrun[currentrun.len]
--currentrun.len
- if (!SA)
+ if (QDELETED(SA))
GLOB.simple_animals[AI_IDLE] -= SA
+ log_world("Found a null in simple_animals list!")
continue
if(!SA.ckey)
diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm
index fca394924d..fb0ddead09 100644
--- a/code/controllers/subsystem/ipintel.dm
+++ b/code/controllers/subsystem/ipintel.dm
@@ -2,13 +2,13 @@ SUBSYSTEM_DEF(ipintel)
name = "XKeyScore"
init_order = INIT_ORDER_XKEYSCORE
flags = SS_NO_FIRE
- var/enabled = 0 //disable at round start to avoid checking reconnects
+ var/enabled = FALSE //disable at round start to avoid checking reconnects
var/throttle = 0
var/errors = 0
var/list/cache = list()
/datum/controller/subsystem/ipintel/Initialize(timeofday, zlevel)
- enabled = 1
+ enabled = TRUE
. = ..()
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 779ee2fbac..165f6e9d81 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -691,21 +691,29 @@ SUBSYSTEM_DEF(job)
if(!permitted)
continue
var/obj/item/I = new G.path
- if(I && length(i[LOADOUT_COLOR])) //handle loadout colors
- //handle polychromic items
- if((G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC) && length(G.loadout_initial_colors))
- var/datum/element/polychromic/polychromic = I.comp_lookup["item_worn_overlays"] //stupid way to do it but GetElement does not work for this
- if(polychromic && istype(polychromic))
- var/list/polychromic_entry = polychromic.colors_by_atom[I]
- if(polychromic_entry)
- if(polychromic.suits_with_helmet_typecache[I.type]) //is this one of those toggleable hood/helmet things?
- polychromic.connect_helmet(I,i[LOADOUT_COLOR])
- polychromic.colors_by_atom[I] = i[LOADOUT_COLOR]
- I.update_icon()
- else
- //handle non-polychromic items (they only have one color)
- I.add_atom_colour(i[LOADOUT_COLOR][1], FIXED_COLOUR_PRIORITY)
- I.update_icon()
+ if(I)
+ if(length(i[LOADOUT_COLOR])) //handle loadout colors
+ //handle polychromic items
+ if((G.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC) && length(G.loadout_initial_colors))
+ var/datum/element/polychromic/polychromic = I.comp_lookup["item_worn_overlays"] //stupid way to do it but GetElement does not work for this
+ if(polychromic && istype(polychromic))
+ var/list/polychromic_entry = polychromic.colors_by_atom[I]
+ if(polychromic_entry)
+ if(polychromic.suits_with_helmet_typecache[I.type]) //is this one of those toggleable hood/helmet things?
+ polychromic.connect_helmet(I,i[LOADOUT_COLOR])
+ polychromic.colors_by_atom[I] = i[LOADOUT_COLOR]
+ I.update_icon()
+ else
+ //handle non-polychromic items (they only have one color)
+ I.add_atom_colour(i[LOADOUT_COLOR][1], FIXED_COLOUR_PRIORITY)
+ I.update_icon()
+ //when inputting the data it's already sanitized
+ if(i[LOADOUT_CUSTOM_NAME])
+ var/custom_name = i[LOADOUT_CUSTOM_NAME]
+ I.name = custom_name
+ if(i[LOADOUT_CUSTOM_DESCRIPTION])
+ var/custom_description = i[LOADOUT_CUSTOM_DESCRIPTION]
+ I.desc = custom_description
if(!M.equip_to_slot_if_possible(I, G.slot, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // If the job's dresscode compliant, try to put it in its slot, first
if(iscarbon(M))
var/mob/living/carbon/C = M
diff --git a/code/controllers/subsystem/jukeboxes.dm b/code/controllers/subsystem/jukeboxes.dm
index 3ebeaf71ea..7be6d44a7f 100644
--- a/code/controllers/subsystem/jukeboxes.dm
+++ b/code/controllers/subsystem/jukeboxes.dm
@@ -94,8 +94,8 @@ SUBSYSTEM_DEF(jukeboxes)
stack_trace("Nonexistant or invalid object associated with jukebox.")
continue
var/sound/song_played = sound(juketrack.song_path)
- var/area/currentarea = get_area(jukebox)
var/turf/currentturf = get_turf(jukebox)
+ var/area/currentarea = get_area(jukebox)
var/list/hearerscache = hearers(7, jukebox)
song_played.falloff = jukeinfo[4]
@@ -116,7 +116,6 @@ SUBSYSTEM_DEF(jukeboxes)
inrange = TRUE
else
song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame.
-
M.playsound_local(currentturf, null, 100, channel = jukeinfo[2], S = song_played, envwet = (inrange ? -250 : 0), envdry = (inrange ? 0 : -10000))
CHECK_TICK
return
diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm
index e193c68536..19345f3c8d 100644
--- a/code/controllers/subsystem/lighting.dm
+++ b/code/controllers/subsystem/lighting.dm
@@ -6,6 +6,7 @@ SUBSYSTEM_DEF(lighting)
name = "Lighting"
wait = 2
init_order = INIT_ORDER_LIGHTING
+ flags = SS_TICKER
/datum/controller/subsystem/lighting/stat_entry(msg)
msg = "L:[length(GLOB.lighting_update_lights)]|C:[length(GLOB.lighting_update_corners)]|O:[length(GLOB.lighting_update_objects)]"
diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm
index f356009569..23190574d8 100644
--- a/code/controllers/subsystem/machines.dm
+++ b/code/controllers/subsystem/machines.dm
@@ -2,6 +2,7 @@ SUBSYSTEM_DEF(machines)
name = "Machines"
init_order = INIT_ORDER_MACHINES
flags = SS_KEEP_TIMING
+ wait = 2 SECONDS
var/list/processing = list()
var/list/currentrun = list()
var/list/powernets = list()
@@ -27,7 +28,7 @@ SUBSYSTEM_DEF(machines)
return ..()
-/datum/controller/subsystem/machines/fire(resumed = 0)
+/datum/controller/subsystem/machines/fire(resumed = FALSE)
if (!resumed)
for(var/datum/powernet/Powernet in powernets)
Powernet.reset() //reset the power state.
@@ -36,11 +37,10 @@ SUBSYSTEM_DEF(machines)
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
- var/seconds = wait * 0.1
while(currentrun.len)
var/obj/machinery/thing = currentrun[currentrun.len]
currentrun.len--
- if(!QDELETED(thing) && thing.process(seconds) != PROCESS_KILL)
+ if(!QDELETED(thing) && thing.process(wait * 0.1) != PROCESS_KILL)
if(thing.use_power)
thing.auto_use_power() //add back the power state
else
diff --git a/code/controllers/subsystem/minimum_spawns.dm b/code/controllers/subsystem/minimum_spawns.dm
index caab2b1949..b9d19b6cd2 100644
--- a/code/controllers/subsystem/minimum_spawns.dm
+++ b/code/controllers/subsystem/minimum_spawns.dm
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(min_spawns)
name = "Minimum Spawns" /// this hot steaming pile of garbage makes sure theres a minimum of tendrils scattered around
init_order = INIT_ORDER_DEFAULT
- flags = SS_BACKGROUND | SS_NO_FIRE | SS_ALWAYS_SHOW_STAT
+ flags = SS_BACKGROUND | SS_NO_FIRE
wait = 2
var/where_we_droppin_boys_iterations = 0
var/snaxi_snowflake_check = FALSE
@@ -71,7 +71,7 @@ GLOBAL_LIST_INIT(minimum_snow_under_spawns, list(
continue
if(typesof(/turf/open/lava) in orange(9, TT))
continue
- valid_mining_turfs_2.Add(TT)
+ valid_mining_turfs_2.Add(TT)
else
for(var/z_level in SSmapping.levels_by_trait(ZTRAIT_LAVA_RUINS))
for(var/turf/TT in Z_TURFS(z_level))
@@ -103,14 +103,14 @@ GLOBAL_LIST_INIT(minimum_snow_under_spawns, list(
for(var/mob/living/simple_animal/hostile/megafauna/H in urange(70,RT)) //prevents mob clumps
if((istype(MS_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT, H) <= 70)
active_spawns.Add(MS_tospawn)
- continue //let's try not to dump megas too close to each other?
+ continue //let's try not to dump megas too close to each other?
if((istype(MS_tospawn, /obj/structure/spawner)) && get_dist(RT, H) <= 40)
active_spawns.Add(MS_tospawn)
continue //let's at least /try/ to space these out?
for(var/obj/structure/spawner/LT in urange(70,RT)) //prevents tendril/mega clumps
if((istype(MS_tospawn, /mob/living/simple_animal/hostile/megafauna)) && get_dist(RT, LT) <= 70)
active_spawns.Add(MS_tospawn)
- continue //let's try not to dump megas too close to each other?
+ continue //let's try not to dump megas too close to each other?
if((istype(MS_tospawn, /obj/structure/spawner)) && get_dist(RT, LT) <= 40)
active_spawns.Add(MS_tospawn)
continue //let's at least /try/ to space these out?
@@ -127,7 +127,7 @@ GLOBAL_LIST_INIT(minimum_snow_under_spawns, list(
for(var/mob/living/simple_animal/hostile/H in urange(70,RT2)) //prevents mob clumps
if((istype(MS2_tospawn, /mob/living/simple_animal/hostile/megafauna) || ismegafauna(H)) && get_dist(RT2, H) <= 70)
active_spawns_2.Add(MS2_tospawn)
- continue //let's try not to dump megas too close to each other?
+ continue //let's try not to dump megas too close to each other?
if((istype(MS2_tospawn, /obj/structure/spawner)) && get_dist(RT2, H) <= 40)
active_spawns_2.Add(MS2_tospawn)
continue //let's at least /try/ to space these out?
diff --git a/code/controllers/subsystem/minor_mapping.dm b/code/controllers/subsystem/minor_mapping.dm
index bd950e453e..d6cbf99f97 100644
--- a/code/controllers/subsystem/minor_mapping.dm
+++ b/code/controllers/subsystem/minor_mapping.dm
@@ -1,3 +1,5 @@
+#define PROB_MOUSE_SPAWN 98
+
SUBSYSTEM_DEF(minor_mapping)
name = "Minor Mapping"
init_order = INIT_ORDER_MINOR_MAPPING
@@ -5,29 +7,43 @@ SUBSYSTEM_DEF(minor_mapping)
/datum/controller/subsystem/minor_mapping/Initialize(timeofday)
trigger_migration(CONFIG_GET(number/mice_roundstart))
+ // place_satchels()
return ..()
/datum/controller/subsystem/minor_mapping/proc/trigger_migration(num_mice=10)
var/list/exposed_wires = find_exposed_wires()
- var/mob/living/simple_animal/mouse/M
+ var/mob/living/simple_animal/mouse/mouse
var/turf/proposed_turf
while((num_mice > 0) && exposed_wires.len)
proposed_turf = pick_n_take(exposed_wires)
- if(!M)
- M = new(proposed_turf)
- else
- M.forceMove(proposed_turf)
- if(M.environment_is_safe())
- num_mice -= 1
- M = null
+ if(prob(PROB_MOUSE_SPAWN))
+ if(!mouse)
+ mouse = new(proposed_turf)
+ else
+ mouse.forceMove(proposed_turf)
+ // else
+ // mouse = new /mob/living/simple_animal/hostile/regalrat/controlled(proposed_turf)
+ if(mouse.environment_is_safe())
+ num_mice -= 1
+ mouse = null
+
+// /datum/controller/subsystem/minor_mapping/proc/place_satchels(amount=10)
+// var/list/turfs = find_satchel_suitable_turfs()
+
+// while(turfs.len && amount > 0)
+// var/turf/T = pick_n_take(turfs)
+// var/obj/item/storage/backpack/satchel/flat/F = new(T)
+
+// SEND_SIGNAL(F, COMSIG_OBJ_HIDE, T.intact)
+// amount--
/proc/find_exposed_wires()
var/list/exposed_wires = list()
- exposed_wires.Cut()
+
var/list/all_turfs
- for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
all_turfs += block(locate(1,1,z), locate(world.maxx,world.maxy,z))
for(var/turf/open/floor/plating/T in all_turfs)
if(is_blocked_turf(T))
@@ -36,3 +52,15 @@ SUBSYSTEM_DEF(minor_mapping)
exposed_wires += T
return shuffle(exposed_wires)
+
+// /proc/find_satchel_suitable_turfs()
+// var/list/suitable = list()
+
+// for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+// for(var/t in block(locate(1,1,z), locate(world.maxx,world.maxy,z)))
+// if(isfloorturf(t) && !isplatingturf(t))
+// suitable += t
+
+// return shuffle(suitable)
+
+#undef PROB_MOUSE_SPAWN
diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm
index b42a1e6b7e..74b60783c3 100644
--- a/code/controllers/subsystem/overlays.dm
+++ b/code/controllers/subsystem/overlays.dm
@@ -48,9 +48,16 @@ SUBSYSTEM_DEF(overlays)
for (var/thing in queue)
count++
if(thing)
- STAT_START_STOPWATCH
var/atom/A = thing
+ if(A.overlays.len >= MAX_ATOM_OVERLAYS)
+ //Break it real GOOD
+ stack_trace("Too many overlays on [A.type] - [A.overlays.len], refusing to update and cutting")
+ A.overlays.Cut()
+ continue
+ STAT_START_STOPWATCH
COMPILE_OVERLAYS(A)
+ UNSETEMPTY(A.add_overlays)
+ UNSETEMPTY(A.remove_overlays)
STAT_STOP_STOPWATCH
STAT_LOG_ENTRY(stats, A.type)
if(mc_check)
@@ -117,9 +124,8 @@ SUBSYSTEM_DEF(overlays)
#define QUEUE_FOR_COMPILE flags_1 |= OVERLAY_QUEUED_1; SSoverlays.queue += src;
/atom/proc/cut_overlays()
LAZYINITLIST(remove_overlays)
- LAZYINITLIST(add_overlays)
remove_overlays = overlays.Copy()
- add_overlays.Cut()
+ add_overlays = null
//If not already queued for work and there are overlays to remove
if(NOT_QUEUED_ALREADY && remove_overlays.len)
@@ -129,7 +135,7 @@ SUBSYSTEM_DEF(overlays)
if(!overlays)
return
overlays = build_appearance_list(overlays)
- LAZYINITLIST(add_overlays) //always initialized after this point
+ LAZYINITLIST(add_overlays)
LAZYINITLIST(remove_overlays)
var/a_len = add_overlays.len
var/r_len = remove_overlays.len
@@ -140,8 +146,9 @@ SUBSYSTEM_DEF(overlays)
var/fr_len = remove_overlays.len
//If not already queued and there is work to be done
- if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len))
+ if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len ))
QUEUE_FOR_COMPILE
+ UNSETEMPTY(add_overlays)
/atom/proc/add_overlay(list/overlays)
if(!overlays)
diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm
index 64299fda38..7096c667e1 100644
--- a/code/controllers/subsystem/parallax.dm
+++ b/code/controllers/subsystem/parallax.dm
@@ -7,13 +7,21 @@ SUBSYSTEM_DEF(parallax)
var/list/currentrun
var/planet_x_offset = 128
var/planet_y_offset = 128
+ var/random_layer
+ var/random_parallax_color
-/datum/controller/subsystem/parallax/Initialize(timeofday)
+
+//These are cached per client so needs to be done asap so people joining at roundstart do not miss these.
+/datum/controller/subsystem/parallax/PreInit()
. = ..()
+ if(prob(70)) //70% chance to pick a special extra layer
+ random_layer = pick(/obj/screen/parallax_layer/random/space_gas, /obj/screen/parallax_layer/random/asteroids)
+ random_parallax_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)//Special color for random_layer1. Has to be done here so everyone sees the same color. [COLOR_SILVER]
planet_y_offset = rand(100, 160)
planet_x_offset = rand(100, 160)
-/datum/controller/subsystem/parallax/fire(resumed = 0)
+
+/datum/controller/subsystem/parallax/fire(resumed = FALSE)
if (!resumed)
src.currentrun = GLOB.clients.Copy()
@@ -21,24 +29,27 @@ SUBSYSTEM_DEF(parallax)
var/list/currentrun = src.currentrun
while(length(currentrun))
- var/client/C = currentrun[currentrun.len]
+ var/client/processing_client = currentrun[currentrun.len]
currentrun.len--
- if (!C || !C.eye)
+ if (QDELETED(processing_client) || !processing_client.eye)
if (MC_TICK_CHECK)
return
continue
- var/atom/movable/A = C.eye
- if(!istype(A))
- continue
- for (A; isloc(A.loc) && !isturf(A.loc); A = A.loc);
- if(A != C.movingmob)
- if(C.movingmob != null)
- C.movingmob.client_mobs_in_contents -= C.mob
- UNSETEMPTY(C.movingmob.client_mobs_in_contents)
- LAZYINITLIST(A.client_mobs_in_contents)
- A.client_mobs_in_contents += C.mob
- C.movingmob = A
+ var/atom/movable/movable_eye = processing_client.eye
+ if(!istype(movable_eye))
+ continue
+
+ for (movable_eye; isloc(movable_eye.loc) && !isturf(movable_eye.loc); movable_eye = movable_eye.loc);
+
+ if(movable_eye == processing_client.movingmob)
+ if (MC_TICK_CHECK)
+ return
+ continue
+ if(!isnull(processing_client.movingmob))
+ LAZYREMOVE(processing_client.movingmob.client_mobs_in_contents, processing_client.mob)
+ LAZYADD(movable_eye.client_mobs_in_contents, processing_client.mob)
+ processing_client.movingmob = movable_eye
if (MC_TICK_CHECK)
return
currentrun = null
diff --git a/code/controllers/subsystem/pathfinder.dm b/code/controllers/subsystem/pathfinder.dm
index 8e1cf946ae..ccbea79306 100644
--- a/code/controllers/subsystem/pathfinder.dm
+++ b/code/controllers/subsystem/pathfinder.dm
@@ -18,7 +18,7 @@ SUBSYSTEM_DEF(pathfinder)
var/free
var/list/flow
-/datum/flowcache/New(var/n)
+/datum/flowcache/New(n)
. = ..()
lcount = n
run = 0
diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm
index 741d1dd72a..e8faf80e5d 100644
--- a/code/controllers/subsystem/persistence/_persistence.dm
+++ b/code/controllers/subsystem/persistence/_persistence.dm
@@ -58,6 +58,7 @@ SUBSYSTEM_DEF(persistence)
if(CONFIG_GET(flag/use_antag_rep))
LoadAntagReputation()
LoadRandomizedRecipes()
+ LoadPaintings()
/**
* Saves persistent data relevant to the server: Configurations, past gamemodes, votes, antag rep, etc
diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm
index 9622e02146..1b30ca44c2 100644
--- a/code/controllers/subsystem/processing/fastprocess.dm
+++ b/code/controllers/subsystem/processing/fastprocess.dm
@@ -1,6 +1,4 @@
-//Fires five times every second.
-
PROCESSING_SUBSYSTEM_DEF(fastprocess)
name = "Fast Processing"
- wait = 2
+ wait = 0.2 SECONDS
stat_tag = "FP"
diff --git a/code/controllers/subsystem/processing/nanites.dm b/code/controllers/subsystem/processing/nanites.dm
index c34e7f7806..8a55491f5f 100644
--- a/code/controllers/subsystem/processing/nanites.dm
+++ b/code/controllers/subsystem/processing/nanites.dm
@@ -1,7 +1,7 @@
PROCESSING_SUBSYSTEM_DEF(nanites)
name = "Nanites"
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
- wait = 10
+ wait = 1 SECONDS
var/list/datum/nanite_cloud_backup/cloud_backups = list()
var/list/mob/living/nanite_monitored_mobs = list()
diff --git a/code/controllers/subsystem/processing/obj.dm b/code/controllers/subsystem/processing/obj.dm
index 26021fb267..3566e8a4dc 100644
--- a/code/controllers/subsystem/processing/obj.dm
+++ b/code/controllers/subsystem/processing/obj.dm
@@ -2,4 +2,4 @@ PROCESSING_SUBSYSTEM_DEF(obj)
name = "Objects"
priority = FIRE_PRIORITY_OBJ
flags = SS_NO_INIT
- wait = 20
+ wait = 2 SECONDS
diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm
index 637b04993d..5cefd3a148 100644
--- a/code/controllers/subsystem/processing/processing.dm
+++ b/code/controllers/subsystem/processing/processing.dm
@@ -1,10 +1,10 @@
-//Used to process objects. Fires once every second.
+//Used to process objects.
SUBSYSTEM_DEF(processing)
name = "Processing"
priority = FIRE_PRIORITY_PROCESS
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
- wait = 10
+ wait = 1 SECONDS
var/stat_tag = "P" //Used for logging
var/list/processing = list()
@@ -14,9 +14,10 @@ SUBSYSTEM_DEF(processing)
msg = "[stat_tag]:[length(processing)]"
return ..()
-/datum/controller/subsystem/processing/fire(resumed = 0)
+/datum/controller/subsystem/processing/fire(resumed = FALSE)
if (!resumed)
currentrun = processing.Copy()
+ var/delta_time = (flags & SS_TICKER)? (wait * world.tick_lag * 0.1) : (wait * 0.1)
//cache for sanic speed (lists are references anyways)
var/list/current_run = currentrun
@@ -25,12 +26,26 @@ SUBSYSTEM_DEF(processing)
current_run.len--
if(QDELETED(thing))
processing -= thing
- else if(thing.process(wait) == PROCESS_KILL)
+ else if(thing.process(delta_time) == PROCESS_KILL)
// fully stop so that a future START_PROCESSING will work
STOP_PROCESSING(src, thing)
if (MC_TICK_CHECK)
return
-/datum/proc/process()
- set waitfor = 0
+
+/**
+ * This proc is called on a datum on every "cycle" if it is being processed by a subsystem. The time between each cycle is determined by the subsystem's "wait" setting.
+ * You can start and stop processing a datum using the START_PROCESSING and STOP_PROCESSING defines.
+ *
+ * Since the wait setting of a subsystem can be changed at any time, it is important that any rate-of-change that you implement in this proc is multiplied by the delta_time that is sent as a parameter,
+ * Additionally, any "prob" you use in this proc should instead use the DT_PROB define to make sure that the final probability per second stays the same even if the subsystem's wait is altered.
+ * Examples where this must be considered:
+ * - Implementing a cooldown timer, use `mytimer -= delta_time`, not `mytimer -= 1`. This way, `mytimer` will always have the unit of seconds
+ * - Damaging a mob, do `L.adjustFireLoss(20 * delta_time)`, not `L.adjustFireLoss(20)`. This way, the damage per second stays constant even if the wait of the subsystem is changed
+ * - Probability of something happening, do `if(DT_PROB(25, delta_time))`, not `if(prob(25))`. This way, if the subsystem wait is e.g. lowered, there won't be a higher chance of this event happening per second
+ *
+ * If you override this do not call parent, as it will return PROCESS_KILL. This is done to prevent objects that dont override process() from staying in the processing list
+ */
+/datum/proc/process(delta_time)
+ set waitfor = FALSE
return PROCESS_KILL
diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm
index c57bac5526..74cd53b0ae 100644
--- a/code/controllers/subsystem/processing/quirks.dm
+++ b/code/controllers/subsystem/processing/quirks.dm
@@ -5,8 +5,8 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
name = "Quirks"
init_order = INIT_ORDER_QUIRKS
flags = SS_BACKGROUND
- wait = 10
runlevels = RUNLEVEL_GAME
+ wait = 1 SECONDS
var/list/quirks = list() //Assoc. list of all roundstart quirk datum types; "name" = /path/
var/list/quirk_names_by_path = list()
diff --git a/code/controllers/subsystem/profiler.dm b/code/controllers/subsystem/profiler.dm
index 81fa77dc6c..7533e9663f 100644
--- a/code/controllers/subsystem/profiler.dm
+++ b/code/controllers/subsystem/profiler.dm
@@ -18,7 +18,7 @@ SUBSYSTEM_DEF(profiler)
if(CONFIG_GET(flag/auto_profile))
StartProfiling()
else
- StopProfiling() //Stop the early start from world/New
+ StopProfiling() //Stop the early start profiler
return ..()
/datum/controller/subsystem/profiler/fire()
@@ -31,12 +31,23 @@ SUBSYSTEM_DEF(profiler)
return ..()
/datum/controller/subsystem/profiler/proc/StartProfiling()
+#if DM_BUILD < 1506
+ stack_trace("Auto profiling unsupported on this byond version")
+ CONFIG_SET(flag/auto_profile, FALSE)
+#else
world.Profile(PROFILE_START)
+#endif
/datum/controller/subsystem/profiler/proc/StopProfiling()
+#if DM_BUILD >= 1506
world.Profile(PROFILE_STOP)
+#endif
/datum/controller/subsystem/profiler/proc/DumpFile()
+#if DM_BUILD < 1506
+ stack_trace("Auto profiling unsupported on this byond version")
+ CONFIG_SET(flag/auto_profile, FALSE)
+#else
var/timer = TICK_USAGE_REAL
var/current_profile_data = world.Profile(PROFILE_REFRESH,format="json")
fetch_cost = MC_AVERAGE(fetch_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
@@ -49,3 +60,4 @@ SUBSYSTEM_DEF(profiler)
timer = TICK_USAGE_REAL
WRITE_FILE(json_file, current_profile_data)
write_cost = MC_AVERAGE(write_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+#endif
diff --git a/code/controllers/subsystem/radiation.dm b/code/controllers/subsystem/radiation.dm
index f29fe72e80..2d764d84dd 100644
--- a/code/controllers/subsystem/radiation.dm
+++ b/code/controllers/subsystem/radiation.dm
@@ -1,6 +1,7 @@
PROCESSING_SUBSYSTEM_DEF(radiation)
name = "Radiation"
flags = SS_NO_INIT | SS_BACKGROUND
+ wait = 1 SECONDS
var/list/warned_atoms = list()
@@ -13,5 +14,5 @@ PROCESSING_SUBSYSTEM_DEF(radiation)
warned_atoms[ref] = TRUE
var/atom/master = contamination.parent
SSblackbox.record_feedback("tally", "contaminated", 1, master.type)
- var/msg = "has become contamintaed with enough radiation to contaminate other objects. || Source: [contamination.source] || Strength: [contamination.strength]"
+ var/msg = "has become contaminated with enough radiation to contaminate other objects. || Source: [contamination.source] || Strength: [contamination.strength]"
master.investigate_log(msg, INVESTIGATE_RADIATION)
diff --git a/code/controllers/subsystem/runechat.dm b/code/controllers/subsystem/runechat.dm
index ec296e7d32..9bd665e5ee 100644
--- a/code/controllers/subsystem/runechat.dm
+++ b/code/controllers/subsystem/runechat.dm
@@ -6,15 +6,15 @@
#define BUCKET_LIMIT (world.time + TICKS2DS(min(BUCKET_LEN - (SSrunechat.practical_offset - DS2TICKS(world.time - SSrunechat.head_offset)) - 1, BUCKET_LEN - 1)))
/**
- * # Runechat Subsystem
- *
- * Maintains a timer-like system to handle destruction of runechat messages. Much of this code is modeled
- * after or adapted from the timer subsystem.
- *
- * Note that this has the same structure for storing and queueing messages as the timer subsystem does
- * for handling timers: the bucket_list is a list of chatmessage datums, each of which are the head
- * of a circularly linked list. Any given index in bucket_list could be null, representing an empty bucket.
- */
+ * # Runechat Subsystem
+ *
+ * Maintains a timer-like system to handle destruction of runechat messages. Much of this code is modeled
+ * after or adapted from the timer subsystem.
+ *
+ * Note that this has the same structure for storing and queueing messages as the timer subsystem does
+ * for handling timers: the bucket_list is a list of chatmessage datums, each of which are the head
+ * of a circularly linked list. Any given index in bucket_list could be null, representing an empty bucket.
+ */
SUBSYSTEM_DEF(runechat)
name = "Runechat"
flags = SS_TICKER | SS_NO_INIT
@@ -131,14 +131,14 @@ SUBSYSTEM_DEF(runechat)
bucket_resolution = world.tick_lag
/**
- * Enters the runechat subsystem with this chatmessage, inserting it into the end-of-life queue
- *
- * This will also account for a chatmessage already being registered, and in which case
- * the position will be updated to remove it from the previous location if necessary
- *
- * Arguments:
- * * new_sched_destruction Optional, when provided is used to update an existing message with the new specified time
- */
+ * Enters the runechat subsystem with this chatmessage, inserting it into the end-of-life queue
+ *
+ * This will also account for a chatmessage already being registered, and in which case
+ * the position will be updated to remove it from the previous location if necessary
+ *
+ * Arguments:
+ * * new_sched_destruction Optional, when provided is used to update an existing message with the new specified time
+ */
/datum/chatmessage/proc/enter_subsystem(new_sched_destruction = 0)
// Get local references from subsystem as they are faster to access than the datum references
var/list/bucket_list = SSrunechat.bucket_list
@@ -169,7 +169,7 @@ SUBSYSTEM_DEF(runechat)
// Handle insertion into the secondary queue if the required time is outside our tracked amounts
if (scheduled_destruction >= BUCKET_LIMIT)
- BINARY_INSERT(src, SSrunechat.second_queue, datum/chatmessage, src, scheduled_destruction, COMPARE_KEY)
+ BINARY_INSERT(src, SSrunechat.second_queue, /datum/chatmessage, src, scheduled_destruction, COMPARE_KEY)
return
// Get bucket position and a local reference to the datum var, it's faster to access this way
@@ -194,8 +194,8 @@ SUBSYSTEM_DEF(runechat)
/**
- * Removes this chatmessage datum from the runechat subsystem
- */
+ * Removes this chatmessage datum from the runechat subsystem
+ */
/datum/chatmessage/proc/leave_subsystem()
// Attempt to find the bucket that contains this chat message
var/bucket_pos = BUCKET_POS(scheduled_destruction)
diff --git a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm
index 2427fbd277..7eb81003f1 100644
--- a/code/controllers/subsystem/server_maint.dm
+++ b/code/controllers/subsystem/server_maint.dm
@@ -56,12 +56,13 @@ SUBSYSTEM_DEF(server_maint)
for(var/I in currentrun)
var/client/C = I
//handle kicking inactive players
- if(round_started && kick_inactive && C.is_afk(afk_period))
+ if(round_started && kick_inactive && !C.holder && C.is_afk(afk_period))
var/cmob = C.mob
- if(!(isobserver(cmob) || (isdead(cmob) && C.holder)))
+ if (!isnewplayer(cmob) || !SSticker.queued_players.Find(cmob))
log_access("AFK: [key_name(C)]")
- to_chat(C, "You have been inactive for more than [DisplayTimeText(afk_period)] and have been disconnected.")
- qdel(C)
+ to_chat(C, "You have been inactive for more than [DisplayTimeText(afk_period)] and have been disconnected.
You may reconnect via the button in the file menu or by clicking here to reconnect.")
+ QDEL_IN(C, 1) //to ensure they get our message before getting disconnected
+ continue
if (!(!C || world.time - C.connection_time < PING_BUFFER_TIME || C.inactivity >= (wait-1)))
winset(C, null, "command=.update_ping+[world.time+world.tick_lag*TICK_USAGE_REAL/100]")
@@ -83,4 +84,15 @@ SUBSYSTEM_DEF(server_maint)
if(tgsversion)
SSblackbox.record_feedback("text", "server_tools", 1, tgsversion.raw_parameter)
+
+/datum/controller/subsystem/server_maint/proc/UpdateHubStatus()
+ // if(!CONFIG_GET(flag/hub) || !CONFIG_GET(number/max_hub_pop))
+ // return FALSE //no point, hub / auto hub controls are disabled
+
+ // var/max_pop = CONFIG_GET(number/max_hub_pop)
+
+ // if(GLOB.clients.len > max_pop)
+ // world.update_hub_visibility(FALSE)
+ // else
+ // world.update_hub_visibility(TRUE)
#undef PING_BUFFER_TIME
diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm
index 5e7c5e6545..fa9ba3c472 100644
--- a/code/controllers/subsystem/sounds.dm
+++ b/code/controllers/subsystem/sounds.dm
@@ -5,34 +5,48 @@ SUBSYSTEM_DEF(sounds)
flags = SS_NO_FIRE
init_order = INIT_ORDER_SOUNDS
var/static/using_channels_max = CHANNEL_HIGHEST_AVAILABLE //BYOND max channels
+ /// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
+ var/static/random_channels_min = 50
// Hey uh these two needs to be initialized fast because the whole "things get deleted before init" thing.
- /// Assoc list, "[channel]" = either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel
- var/list/using_channels = list()
+ /// Assoc list, `"[channel]" =` either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel
+ var/list/using_channels
/// Assoc list datum = list(channel1, channel2, ...) for what channels something reserved.
- var/list/using_channels_by_datum = list()
- /// List of all available channels with associations set to TRUE for fast lookups/allocation.
- var/list/available_channels
+ var/list/using_channels_by_datum
+ // Special datastructure for fast channel management
+ /// List of all channels as numbers
+ var/list/channel_list
+ /// Associative list of all reserved channels associated to their position. `"[channel_number]" =` index as number
+ var/list/reserved_channels
+ /// lower iteration position - Incremented and looped to get "random" sound channels for normal sounds. The channel at this index is returned when asking for a random channel.
+ var/channel_random_low
+ /// higher reserve position - decremented and incremented to reserve sound channels, anything above this is reserved. The channel at this index is the highest unreserved channel.
+ var/channel_reserve_high
/datum/controller/subsystem/sounds/Initialize()
setup_available_channels()
return ..()
/datum/controller/subsystem/sounds/proc/setup_available_channels()
- available_channels = list()
+ channel_list = list()
+ reserved_channels = list()
+ using_channels = list()
+ using_channels_by_datum = list()
for(var/i in 1 to using_channels_max)
- available_channels[num2text(i)] = TRUE
+ channel_list += i
+ channel_random_low = 1
+ channel_reserve_high = length(channel_list)
/// Removes a channel from using list.
/datum/controller/subsystem/sounds/proc/free_sound_channel(channel)
- channel = num2text(channel)
- var/using = using_channels[channel]
- using_channels -= channel
- if(using)
+ var/text_channel = num2text(channel)
+ var/using = using_channels[text_channel]
+ using_channels -= text_channel
+ if(using != TRUE) // datum channel
using_channels_by_datum[using] -= channel
if(!length(using_channels_by_datum[using]))
using_channels_by_datum -= using
- available_channels[channel] = TRUE
+ free_channel(channel)
/// Frees all the channels a datum is using.
/datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D)
@@ -40,8 +54,8 @@ SUBSYSTEM_DEF(sounds)
if(!L)
return
for(var/channel in L)
- using_channels -= channel
- available_channels[channel] = TRUE
+ using_channels -= num2text(channel)
+ free_channel(channel)
using_channels_by_datum -= D
/// Frees all datumless channels
@@ -50,42 +64,72 @@ SUBSYSTEM_DEF(sounds)
/// NO AUTOMATIC CLEANUP - If you use this, you better manually free it later! Returns an integer for channel.
/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless()
- var/channel = random_available_channel_text()
- if(!channel) //oh no..
+ . = reserve_channel()
+ if(!.) //oh no..
return FALSE
- available_channels -= channel
- using_channels[channel] = DATUMLESS
+ var/text_channel = num2text(.)
+ using_channels[text_channel] = DATUMLESS
LAZYINITLIST(using_channels_by_datum[DATUMLESS])
- using_channels_by_datum[DATUMLESS] += channel
- return text2num(channel)
+ using_channels_by_datum[DATUMLESS] += .
/// Reserves a channel for a datum. Automatic cleanup only when the datum is deleted. Returns an integer for channel.
/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D)
if(!D) //i don't like typechecks but someone will fuck it up
CRASH("Attempted to reserve sound channel without datum using the managed proc.")
- var/channel = random_available_channel_text()
- if(!channel)
+ .= reserve_channel()
+ if(!.)
return FALSE
- available_channels -= channel
- using_channels[channel] = D
+ var/text_channel = num2text(.)
+ using_channels[text_channel] = D
LAZYINITLIST(using_channels_by_datum[D])
- using_channels_by_datum[D] += channel
- return text2num(channel)
+ using_channels_by_datum[D] += .
+
+/**
+ * Reserves a channel and updates the datastructure. Private proc.
+ */
+/datum/controller/subsystem/sounds/proc/reserve_channel()
+ PRIVATE_PROC(TRUE)
+ if(channel_reserve_high <= random_channels_min) // out of channels
+ return
+ var/channel = channel_list[channel_reserve_high]
+ reserved_channels[num2text(channel)] = channel_reserve_high--
+ return channel
+
+/**
+ * Frees a channel and updates the datastructure. Private proc.
+ */
+/datum/controller/subsystem/sounds/proc/free_channel(number)
+ PRIVATE_PROC(TRUE)
+ var/text_channel = num2text(number)
+ var/index = reserved_channels[text_channel]
+ if(!index)
+ CRASH("Attempted to (internally) free a channel that wasn't reserved.")
+ reserved_channels -= text_channel
+ // push reserve index up, which makes it now on a channel that is reserved
+ channel_reserve_high++
+ // swap the reserved channel wtih the unreserved channel so the reserve index is now on an unoccupied channel and the freed channel is next to be used.
+ channel_list.Swap(channel_reserve_high, index)
+ // now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index
+ // get it, and update position.
+ var/text_reserved = num2text(channel_list[index])
+ if(!reserved_channels[text_reserved]) //if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
+ return
+ reserved_channels[text_reserved] = index
/// Random available channel, returns text.
/datum/controller/subsystem/sounds/proc/random_available_channel_text()
- return pick(available_channels)
+ if(channel_random_low > channel_reserve_high)
+ channel_random_low = 1
+ . = "[channel_list[channel_random_low++]]"
/// Random available channel, returns number
/datum/controller/subsystem/sounds/proc/random_available_channel()
- return text2num(pick(available_channels))
-
-/// If a channel is available
-/datum/controller/subsystem/sounds/proc/is_channel_available(channel)
- return available_channels[num2text(channel)]
+ if(channel_random_low > channel_reserve_high)
+ channel_random_low = 1
+ . = channel_list[channel_random_low++]
/// How many channels we have left.
/datum/controller/subsystem/sounds/proc/available_channels_left()
- return length(available_channels)
+ return length(channel_list) - random_channels_min
#undef DATUMLESS
diff --git a/code/controllers/subsystem/spacedrift.dm b/code/controllers/subsystem/spacedrift.dm
index c3261df304..e84a70a45f 100644
--- a/code/controllers/subsystem/spacedrift.dm
+++ b/code/controllers/subsystem/spacedrift.dm
@@ -13,7 +13,7 @@ SUBSYSTEM_DEF(spacedrift)
return ..()
-/datum/controller/subsystem/spacedrift/fire(resumed = 0)
+/datum/controller/subsystem/spacedrift/fire(resumed = FALSE)
if (!resumed)
src.currentrun = processing.Copy()
@@ -47,6 +47,7 @@ SUBSYSTEM_DEF(spacedrift)
var/old_dir = AM.dir
var/old_loc = AM.loc
AM.inertia_moving = TRUE
+ AM.set_glide_size(DELAY_TO_GLIDE_SIZE(AM.inertia_move_delay), FALSE)
step(AM, AM.inertia_dir)
AM.inertia_moving = FALSE
AM.inertia_next_move = world.time + AM.inertia_move_delay
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index 13e9ff50a2..d0d5579611 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -203,3 +203,4 @@ SUBSYSTEM_DEF(statpanels)
set hidden = TRUE
statbrowser_ready = TRUE
+ init_verbs()
diff --git a/code/controllers/subsystem/stickyban.dm b/code/controllers/subsystem/stickyban.dm
index 189efa99fe..0c71777bc0 100644
--- a/code/controllers/subsystem/stickyban.dm
+++ b/code/controllers/subsystem/stickyban.dm
@@ -1,34 +1,231 @@
SUBSYSTEM_DEF(stickyban)
- name = "Sticky Ban"
+ name = "PRISM"
init_order = INIT_ORDER_STICKY_BAN
flags = SS_NO_FIRE
var/list/cache = list()
+ var/list/dbcache = list()
+ var/list/confirmed_exempt = list()
+ var/dbcacheexpire = 0
+
/datum/controller/subsystem/stickyban/Initialize(timeofday)
- var/list/bannedkeys = world.GetConfig("ban")
+ if (length(GLOB.stickybanadminexemptions))
+ restore_stickybans()
+ var/list/bannedkeys = sticky_banned_ckeys()
//sanitize the sticky ban list
+
+ //delete db bans that no longer exist in the database and add new legacy bans to the database
+ if (SSdbcore.Connect() || length(SSstickyban.dbcache))
+ if (length(GLOB.stickybanadminexemptions))
+ restore_stickybans()
+ for (var/oldban in (world.GetConfig("ban") - bannedkeys))
+ var/ckey = ckey(oldban)
+ if (ckey != oldban && (ckey in bannedkeys))
+ continue
+
+ var/list/ban = params2list(world.GetConfig("ban", oldban))
+ if (ban && !ban["fromdb"])
+ if (!import_raw_stickyban_to_db(ckey, ban))
+ log_world("Could not import stickyban on [oldban] into the database. Ignoring")
+ continue
+ dbcacheexpire = 0
+ bannedkeys += ckey
+ world.SetConfig("ban", oldban, null)
+
+ if (length(GLOB.stickybanadminexemptions)) //the previous loop can sleep
+ restore_stickybans()
+
for (var/bannedkey in bannedkeys)
var/ckey = ckey(bannedkey)
- var/list/ban = stickyban2list(world.GetConfig("ban", bannedkey))
+ var/list/ban = get_stickyban_from_ckey(bannedkey)
- //byond stores sticky bans by key, that can end up confusing things
- //i also remove it here so that if any stickybans cause a runtime, they just stop existing
- world.SetConfig("ban", bannedkey, null)
+ //byond stores sticky bans by key, that's lame
+ if (ckey != bannedkey)
+ world.SetConfig("ban", bannedkey, null)
if (!ban["ckey"])
ban["ckey"] = ckey
- //storing these can break things and isn't needed for sticky ban tracking
- ban -= "IP"
- ban -= "computer_id"
-
ban["matches_this_round"] = list()
ban["existing_user_matches_this_round"] = list()
ban["admin_matches_this_round"] = list()
+ ban["pending_matches_this_round"] = list()
+
cache[ckey] = ban
-
- for (var/bannedckey in cache)
- world.SetConfig("ban", bannedckey, list2stickyban(cache[bannedckey]))
+ world.SetConfig("ban", ckey, list2stickyban(ban))
return ..()
+
+/datum/controller/subsystem/stickyban/proc/Populatedbcache()
+ var/newdbcache = list() //so if we runtime or the db connection dies we don't kill the existing cache
+
+ // var/datum/db_query/query_stickybans = SSdbcore.NewQuery("SELECT ckey, reason, banning_admin, datetime FROM [format_table_name("stickyban")] ORDER BY ckey")
+ // var/datum/db_query/query_ckey_matches = SSdbcore.NewQuery("SELECT stickyban, matched_ckey, first_matched, last_matched, exempt FROM [format_table_name("stickyban_matched_ckey")] ORDER BY first_matched")
+ // var/datum/db_query/query_cid_matches = SSdbcore.NewQuery("SELECT stickyban, matched_cid, first_matched, last_matched FROM [format_table_name("stickyban_matched_cid")] ORDER BY first_matched")
+ // var/datum/db_query/query_ip_matches = SSdbcore.NewQuery("SELECT stickyban, INET_NTOA(matched_ip), first_matched, last_matched FROM [format_table_name("stickyban_matched_ip")] ORDER BY first_matched")
+
+ var/datum/DBQuery/query_stickybans = SSdbcore.NewQuery("SELECT ckey, reason, banning_admin, datetime FROM [format_table_name("stickyban")] ORDER BY ckey")
+ var/datum/DBQuery/query_ckey_matches = SSdbcore.NewQuery("SELECT stickyban, matched_ckey, first_matched, last_matched, exempt FROM [format_table_name("stickyban_matched_ckey")] ORDER BY first_matched")
+ var/datum/DBQuery/query_cid_matches = SSdbcore.NewQuery("SELECT stickyban, matched_cid, first_matched, last_matched FROM [format_table_name("stickyban_matched_cid")] ORDER BY first_matched")
+ var/datum/DBQuery/query_ip_matches = SSdbcore.NewQuery("SELECT stickyban, INET_NTOA(matched_ip), first_matched, last_matched FROM [format_table_name("stickyban_matched_ip")] ORDER BY first_matched")
+
+ SSdbcore.QuerySelect(list(query_stickybans, query_ckey_matches, query_cid_matches, query_ip_matches))
+
+ if (query_stickybans.last_error)
+ qdel(query_stickybans)
+ qdel(query_ckey_matches)
+ qdel(query_cid_matches)
+ qdel(query_ip_matches)
+ return
+
+ while (query_stickybans.NextRow())
+ var/list/ban = list()
+
+ ban["ckey"] = query_stickybans.item[1]
+ ban["message"] = query_stickybans.item[2]
+ ban["reason"] = "(InGameBan)([query_stickybans.item[3]])"
+ ban["admin"] = query_stickybans.item[3]
+ ban["datetime"] = query_stickybans.item[4]
+ ban["type"] = list("sticky")
+
+ newdbcache["[query_stickybans.item[1]]"] = ban
+
+
+ if (!query_ckey_matches.last_error)
+ while (query_ckey_matches.NextRow())
+ var/list/match = list()
+
+ match["stickyban"] = query_ckey_matches.item[1]
+ match["matched_ckey"] = query_ckey_matches.item[2]
+ match["first_matched"] = query_ckey_matches.item[3]
+ match["last_matched"] = query_ckey_matches.item[4]
+ match["exempt"] = text2num(query_ckey_matches.item[5])
+
+ var/ban = newdbcache[query_ckey_matches.item[1]]
+ if (!ban)
+ continue
+ var/keys = ban[text2num(query_ckey_matches.item[5]) ? "whitelist" : "keys"]
+ if (!keys)
+ keys = ban[text2num(query_ckey_matches.item[5]) ? "whitelist" : "keys"] = list()
+ keys[query_ckey_matches.item[2]] = match
+
+ if (!query_cid_matches.last_error)
+ while (query_cid_matches.NextRow())
+ var/list/match = list()
+
+ match["stickyban"] = query_cid_matches.item[1]
+ match["matched_cid"] = query_cid_matches.item[2]
+ match["first_matched"] = query_cid_matches.item[3]
+ match["last_matched"] = query_cid_matches.item[4]
+
+ var/ban = newdbcache[query_cid_matches.item[1]]
+ if (!ban)
+ continue
+ var/computer_ids = ban["computer_id"]
+ if (!computer_ids)
+ computer_ids = ban["computer_id"] = list()
+ computer_ids[query_cid_matches.item[2]] = match
+
+
+ if (!query_ip_matches.last_error)
+ while (query_ip_matches.NextRow())
+ var/list/match = list()
+
+ match["stickyban"] = query_ip_matches.item[1]
+ match["matched_ip"] = query_ip_matches.item[2]
+ match["first_matched"] = query_ip_matches.item[3]
+ match["last_matched"] = query_ip_matches.item[4]
+
+ var/ban = newdbcache[query_ip_matches.item[1]]
+ if (!ban)
+ continue
+ var/IPs = ban["IP"]
+ if (!IPs)
+ IPs = ban["IP"] = list()
+ IPs[query_ip_matches.item[2]] = match
+
+ dbcache = newdbcache
+ dbcacheexpire = world.time+STICKYBAN_DB_CACHE_TIME
+
+ qdel(query_stickybans)
+ qdel(query_ckey_matches)
+ qdel(query_cid_matches)
+ qdel(query_ip_matches)
+
+
+/datum/controller/subsystem/stickyban/proc/import_raw_stickyban_to_db(ckey, list/ban)
+ . = FALSE
+ if (!ban["admin"])
+ ban["admin"] = "LEGACY"
+ if (!ban["message"])
+ ban["message"] = "Evasion"
+
+ // TODO: USE NEW DB IMPLEMENTATION
+ var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery(
+ "INSERT IGNORE INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES ([ckey], [ban["message"]], ban["admin"]))"
+ )
+
+ if (query_create_stickyban.warn_execute())
+ qdel(query_create_stickyban)
+ return
+ qdel(query_create_stickyban)
+
+ // var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery(
+ // "INSERT IGNORE INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES (:ckey, :message, :admin)",
+ // list("ckey" = ckey, "message" = ban["message"], "admin" = ban["admin"])
+ // )
+ // if (!query_create_stickyban.warn_execute())
+ // qdel(query_create_stickyban)
+ // return
+ // qdel(query_create_stickyban)
+
+ var/list/sqlckeys = list()
+ var/list/sqlcids = list()
+ var/list/sqlips = list()
+
+ if (ban["keys"])
+ var/list/keys = splittext(ban["keys"], ",")
+ for (var/key in keys)
+ var/list/sqlckey = list()
+ sqlckey["stickyban"] = ckey
+ sqlckey["matched_ckey"] = ckey(key)
+ sqlckey["exempt"] = FALSE
+ sqlckeys[++sqlckeys.len] = sqlckey
+
+ if (ban["whitelist"])
+ var/list/keys = splittext(ban["whitelist"], ",")
+ for (var/key in keys)
+ var/list/sqlckey = list()
+ sqlckey["stickyban"] = ckey
+ sqlckey["matched_ckey"] = ckey(key)
+ sqlckey["exempt"] = TRUE
+ sqlckeys[++sqlckeys.len] = sqlckey
+
+ if (ban["computer_id"])
+ var/list/cids = splittext(ban["computer_id"], ",")
+ for (var/cid in cids)
+ var/list/sqlcid = list()
+ sqlcid["stickyban"] = ckey
+ sqlcid["matched_cid"] = cid
+ sqlcids[++sqlcids.len] = sqlcid
+
+ if (ban["IP"])
+ var/list/ips = splittext(ban["IP"], ",")
+ for (var/ip in ips)
+ var/list/sqlip = list()
+ sqlip["stickyban"] = ckey
+ sqlip["matched_ip"] = ip
+ sqlips[++sqlips.len] = sqlip
+
+ if (length(sqlckeys))
+ SSdbcore.MassInsert(format_table_name("stickyban_matched_ckey"), sqlckeys, ignore_errors = TRUE)
+
+ if (length(sqlcids))
+ SSdbcore.MassInsert(format_table_name("stickyban_matched_cid"), sqlcids, ignore_errors = TRUE)
+
+ if (length(sqlips))
+ SSdbcore.MassInsert(format_table_name("stickyban_matched_ip"), sqlips, ignore_errors = TRUE)
+
+
+ return TRUE
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index 8c5abc5469..0fa8035d72 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -57,6 +57,7 @@ SUBSYSTEM_DEF(throwing)
var/dx
var/dy
var/force = MOVE_FORCE_DEFAULT
+ var/gentle = FALSE
var/pure_diagonal
var/diagonal_error
var/datum/callback/callback
@@ -64,15 +65,44 @@ SUBSYSTEM_DEF(throwing)
var/delayed_time = 0
var/last_move = 0
+
+/datum/thrownthing/New(thrownthing, target, target_turf, init_dir, maxrange, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
+ . = ..()
+ src.thrownthing = thrownthing
+ RegisterSignal(thrownthing, COMSIG_PARENT_QDELETING, .proc/on_thrownthing_qdel)
+ src.target = target
+ src.target_turf = target_turf
+ src.init_dir = init_dir
+ src.maxrange = maxrange
+ src.speed = speed
+ src.thrower = thrower
+ src.diagonals_first = diagonals_first
+ src.force = force
+ src.gentle = gentle
+ src.callback = callback
+ src.target_zone = target_zone
+
+
/datum/thrownthing/Destroy()
+ if(HAS_TRAIT_FROM(thrownthing, TRAIT_SPOOKY_THROW, "revenant"))
+ REMOVE_TRAIT(thrownthing, TRAIT_SPOOKY_THROW, "revenant")
SSthrowing.processing -= thrownthing
thrownthing.throwing = null
thrownthing = null
target = null
thrower = null
- callback = null
+ if(callback)
+ QDEL_NULL(callback) //It stores a reference to the thrownthing, its source. Let's clean that.
return ..()
+
+///Defines the datum behavior on the thrownthing's qdeletion event.
+/datum/thrownthing/proc/on_thrownthing_qdel(atom/movable/source, force)
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+
/datum/thrownthing/proc/tick()
var/atom/movable/AM = thrownthing
if (!isturf(AM.loc) || !AM.throwing)
@@ -112,7 +142,7 @@ SUBSYSTEM_DEF(throwing)
finalize()
return
- AM.Move(step, get_dir(AM, step))
+ AM.Move(step, get_dir(AM, step), DELAY_TO_GLIDE_SIZE(1 / speed))
if (!AM.throwing) // we hit something during our move
finalize(hit = TRUE)
@@ -136,15 +166,21 @@ SUBSYSTEM_DEF(throwing)
if (A == target)
hit = TRUE
thrownthing.throw_impact(A, src)
+ if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
+ return //deletion should already be handled by on_thrownthing_qdel()
break
if (!hit)
thrownthing.throw_impact(get_turf(thrownthing), src) // we haven't hit something yet and we still must, let's hit the ground.
+ if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
+ return //deletion should already be handled by on_thrownthing_qdel()
thrownthing.newtonian_move(init_dir)
else
thrownthing.newtonian_move(init_dir)
if(target)
thrownthing.throw_impact(target, src)
+ if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
+ return //deletion should already be handled by on_thrownthing_qdel()
if (callback)
callback.Invoke()
diff --git a/code/controllers/subsystem/time_track.dm b/code/controllers/subsystem/time_track.dm
index 2f4949fc1e..8a0a351d48 100644
--- a/code/controllers/subsystem/time_track.dm
+++ b/code/controllers/subsystem/time_track.dm
@@ -1,7 +1,8 @@
SUBSYSTEM_DEF(time_track)
name = "Time Tracking"
- wait = 1 SECONDS
- flags = SS_NO_INIT|SS_NO_TICK_CHECK
+ wait = 10
+ flags = SS_NO_TICK_CHECK
+ init_order = INIT_ORDER_TIMETRACK
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/time_dilation_current = 0
@@ -16,33 +17,81 @@ SUBSYSTEM_DEF(time_track)
var/last_tick_byond_time = 0
var/last_tick_tickcount = 0
- var/last_measurement = 0
- var/measurement_delay = 60
-
- var/stat_time_text
- var/time_dilation_text
+/datum/controller/subsystem/time_track/Initialize(start_timeofday)
+ . = ..()
+ GLOB.perf_log = "[GLOB.log_directory]/perf-[GLOB.round_id ? GLOB.round_id : "NULL"]-[SSmapping.config?.map_name].csv"
+ log_perf(
+ list(
+ "time",
+ "players",
+ "tidi",
+ "tidi_fastavg",
+ "tidi_avg",
+ "tidi_slowavg",
+ "maptick",
+ "num_timers",
+ "air_turf_cost",
+ "air_eg_cost",
+ "air_highpressure_cost",
+ "air_hotspots_cost",
+ "air_superconductivity_cost",
+ "air_pipenets_cost",
+ "air_rebuilds_cost",
+ "air_turf_count",
+ "air_eg_count",
+ "air_hotspot_count",
+ "air_network_count",
+ "air_delta_count",
+ "air_superconductive_count"
+ )
+ )
/datum/controller/subsystem/time_track/fire()
- stat_time_text = "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]\n\nRound Time: [DisplayTimeText(world.time - SSticker.round_start_time, 1)] \n\nStation Time: [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]\n\n[time_dilation_text]"
- if(++last_measurement == measurement_delay)
- last_measurement = 0
- var/current_realtime = REALTIMEOFDAY
- var/current_byondtime = world.time
- var/current_tickcount = world.time/world.tick_lag
+ var/current_realtime = REALTIMEOFDAY
+ var/current_byondtime = world.time
+ var/current_tickcount = world.time/world.tick_lag
+ GLOB.glide_size_multiplier = (current_byondtime - last_tick_byond_time) / (current_realtime - last_tick_realtime)
- if (!first_run)
- var/tick_drift = max(0, (((current_realtime - last_tick_realtime) - (current_byondtime - last_tick_byond_time)) / world.tick_lag))
+ if(times_fired % 10) // everything else is once every 10 seconds
+ return
- time_dilation_current = tick_drift / (current_tickcount - last_tick_tickcount) * 100
+ if (!first_run)
+ var/tick_drift = max(0, (((current_realtime - last_tick_realtime) - (current_byondtime - last_tick_byond_time)) / world.tick_lag))
- time_dilation_avg_fast = MC_AVERAGE_FAST(time_dilation_avg_fast, time_dilation_current)
- time_dilation_avg = MC_AVERAGE(time_dilation_avg, time_dilation_avg_fast)
- time_dilation_avg_slow = MC_AVERAGE_SLOW(time_dilation_avg_slow, time_dilation_avg)
- else
- first_run = FALSE
- last_tick_realtime = current_realtime
- last_tick_byond_time = current_byondtime
- last_tick_tickcount = current_tickcount
- SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[SQLtime()]" = list("current" = "[time_dilation_current]", "avg_fast" = "[time_dilation_avg_fast]", "avg" = "[time_dilation_avg]", "avg_slow" = "[time_dilation_avg_slow]")))
- time_dilation_text = "Time Dilation: [round(time_dilation_current,1)]% AVG:([round(time_dilation_avg_fast,1)]%, [round(time_dilation_avg,1)]%, [round(time_dilation_avg_slow,1)]%)"
+ time_dilation_current = tick_drift / (current_tickcount - last_tick_tickcount) * 100
+
+ time_dilation_avg_fast = MC_AVERAGE_FAST(time_dilation_avg_fast, time_dilation_current)
+ time_dilation_avg = MC_AVERAGE(time_dilation_avg, time_dilation_avg_fast)
+ time_dilation_avg_slow = MC_AVERAGE_SLOW(time_dilation_avg_slow, time_dilation_avg)
+ else
+ first_run = FALSE
+ last_tick_realtime = current_realtime
+ last_tick_byond_time = current_byondtime
+ last_tick_tickcount = current_tickcount
+ SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[SQLtime()]" = list("current" = "[time_dilation_current]", "avg_fast" = "[time_dilation_avg_fast]", "avg" = "[time_dilation_avg]", "avg_slow" = "[time_dilation_avg_slow]")))
+ log_perf(
+ list(
+ world.time,
+ length(GLOB.clients),
+ time_dilation_current,
+ time_dilation_avg_fast,
+ time_dilation_avg,
+ time_dilation_avg_slow,
+ MAPTICK_LAST_INTERNAL_TICK_USAGE,
+ length(SStimer.timer_id_dict),
+ SSair.cost_turfs,
+ SSair.cost_groups,
+ SSair.cost_highpressure,
+ SSair.cost_hotspots,
+ SSair.cost_superconductivity,
+ SSair.cost_pipenets,
+ SSair.cost_rebuilds,
+ SSair.get_active_turfs(), //does not return a list, which is what we want
+ SSair.get_amt_excited_groups(),
+ length(SSair.hotspots),
+ length(SSair.networks),
+ length(SSair.high_pressure_delta),
+ length(SSair.active_super_conductivity)
+ )
+ )
diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm
index 398e23cdc1..48165be960 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -1,31 +1,51 @@
-#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth)
+/// Controls how many buckets should be kept, each representing a tick. (1 minutes worth)
+#define BUCKET_LEN (world.fps*1*60)
+/// Helper for getting the correct bucket for a given timer
#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
+/// Gets the maximum time at which timers will be invoked from buckets, used for deferring to secondary queue
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
-#define TIMER_ID_MAX (2**24) //max float with integer precision
+/// Max float with integer precision
+#define TIMER_ID_MAX (2**24)
+/**
+ * # Timer Subsystem
+ *
+ * Handles creation, callbacks, and destruction of timed events.
+ *
+ * It is important to understand the buckets used in the timer subsystem are just a series of circular doubly-linked
+ * lists. The object at a given index in bucket_list is a /datum/timedevent, the head of a circular list, which has prev
+ * and next references for the respective elements in that bucket's circular list.
+ */
SUBSYSTEM_DEF(timer)
name = "Timer"
- wait = 1 //SS_TICKER subsystem, so wait is in ticks
+ wait = 1 // SS_TICKER subsystem, so wait is in ticks
init_order = INIT_ORDER_TIMER
-
+ priority = FIRE_PRIORITY_TIMER
flags = SS_TICKER|SS_NO_INIT
- var/list/datum/timedevent/second_queue = list() //awe, yes, you've had first queue, but what about second queue?
+ /// Queue used for storing timers that do not fit into the current buckets
+ var/list/datum/timedevent/second_queue = list()
+ /// A hashlist dictionary used for storing unique timers
var/list/hashes = list()
-
- var/head_offset = 0 //world.time of the first entry in the the bucket.
- var/practical_offset = 1 //index of the first non-empty item in the bucket.
- var/bucket_resolution = 0 //world.tick_lag the bucket was designed for
- var/bucket_count = 0 //how many timers are in the buckets
-
- var/list/bucket_list = list() //list of buckets, each bucket holds every timer that has to run that byond tick.
-
- var/list/timer_id_dict = list() //list of all active timers assoicated to their timer id (for easy lookup)
-
- var/list/clienttime_timers = list() //special snowflake timers that run on fancy pansy "client time"
-
+ /// world.time of the first entry in the bucket list, effectively the 'start time' of the current buckets
+ var/head_offset = 0
+ /// Index of the wrap around pivot for buckets. buckets before this are later running buckets wrapped around from the end of the bucket list.
+ var/practical_offset = 1
+ /// world.tick_lag the bucket was designed for
+ var/bucket_resolution = 0
+ /// How many timers are in the buckets
+ var/bucket_count = 0
+ /// List of buckets, each bucket holds every timer that has to run that byond tick
+ var/list/bucket_list = list()
+ /// List of all active timers associated to their timer ID (for easy lookup)
+ var/list/timer_id_dict = list()
+ /// Special timers that run in real-time, not BYOND time; these are more expensive to run and maintain
+ var/list/clienttime_timers = list()
+ /// Contains the last time that a timer's callback was invoked, or the last tick the SS fired if no timers are being processed
var/last_invoke_tick = 0
+ /// Contains the last time that a warning was issued for not invoking callbacks
var/static/last_invoke_warning = 0
+ /// Boolean operator controlling if the timer SS will automatically reset buckets if it fails to invoke callbacks for an extended period of time
var/static/bucket_auto_reset = TRUE
/datum/controller/subsystem/timer/PreInit()
@@ -38,44 +58,53 @@ SUBSYSTEM_DEF(timer)
return ..()
/datum/controller/subsystem/timer/fire(resumed = FALSE)
+ // Store local references to datum vars as it is faster to access them
var/lit = last_invoke_tick
- var/last_check = world.time - TICKS2DS(BUCKET_LEN*1.5)
var/list/bucket_list = src.bucket_list
+ var/last_check = world.time - TICKS2DS(BUCKET_LEN * 1.5)
+ // If there are no timers being tracked, then consider now to be the last invoked time
if(!bucket_count)
last_invoke_tick = world.time
+ // Check that we have invoked a callback in the last 1.5 minutes of BYOND time,
+ // and throw a warning and reset buckets if this is true
if(lit && lit < last_check && head_offset < last_check && last_invoke_warning < last_check)
last_invoke_warning = world.time
- var/msg = "No regular timers processed in the last [BUCKET_LEN*1.5] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
+ var/msg = "No regular timers processed in the last [BUCKET_LEN * 1.5] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
message_admins(msg)
WARNING(msg)
if(bucket_auto_reset)
bucket_resolution = 0
- log_world("Timer bucket reset. world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
+ var/list/to_log = list("Timer bucket reset. world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
for (var/i in 1 to length(bucket_list))
var/datum/timedevent/bucket_head = bucket_list[i]
if (!bucket_head)
continue
- log_world("Active timers at index [i]:")
-
+ to_log += "Active timers at index [i]:"
var/datum/timedevent/bucket_node = bucket_head
var/anti_loop_check = 1000
do
- log_world(get_timer_debug_string(bucket_node))
+ to_log += get_timer_debug_string(bucket_node)
bucket_node = bucket_node.next
anti_loop_check--
while(bucket_node && bucket_node != bucket_head && anti_loop_check)
- log_world("Active timers in the second_queue queue:")
+
+ to_log += "Active timers in the second_queue queue:"
for(var/I in second_queue)
- log_world(get_timer_debug_string(I))
+ to_log += get_timer_debug_string(I)
- var/next_clienttime_timer_index = 0
- var/len = length(clienttime_timers)
+ // Dump all the logged data to the world log
+ log_world(to_log.Join("\n"))
- for (next_clienttime_timer_index in 1 to len)
+ // Process client-time timers
+ var/static/next_clienttime_timer_index = 0
+ if (next_clienttime_timer_index)
+ clienttime_timers.Cut(1, next_clienttime_timer_index+1)
+ next_clienttime_timer_index = 0
+ for (next_clienttime_timer_index in 1 to length(clienttime_timers))
if (MC_TICK_CHECK)
next_clienttime_timer_index--
break
@@ -86,8 +115,8 @@ SUBSYSTEM_DEF(timer)
var/datum/callback/callBack = ctime_timer.callBack
if (!callBack)
- clienttime_timers.Cut(next_clienttime_timer_index,next_clienttime_timer_index+1)
- CRASH("Invalid timer: [get_timer_debug_string(ctime_timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset], REALTIMEOFDAY: [REALTIMEOFDAY]")
+ CRASH("Invalid timer: [get_timer_debug_string(ctime_timer)] world.time: [world.time], \
+ head_offset: [head_offset], practical_offset: [practical_offset], REALTIMEOFDAY: [REALTIMEOFDAY]")
ctime_timer.spent = REALTIMEOFDAY
callBack.InvokeAsync()
@@ -95,135 +124,93 @@ SUBSYSTEM_DEF(timer)
if(ctime_timer.flags & TIMER_LOOP)
ctime_timer.spent = 0
ctime_timer.timeToRun = REALTIMEOFDAY + ctime_timer.wait
- BINARY_INSERT(ctime_timer, clienttime_timers, datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY)
+ BINARY_INSERT(ctime_timer, clienttime_timers, /datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY)
else
qdel(ctime_timer)
-
+ // Remove invoked client-time timers
if (next_clienttime_timer_index)
clienttime_timers.Cut(1, next_clienttime_timer_index+1)
+ next_clienttime_timer_index = 0
if (MC_TICK_CHECK)
return
- var/static/list/spent = list()
- var/static/datum/timedevent/timer
+ // Check for when we need to loop the buckets, this occurs when
+ // the head_offset is approaching BUCKET_LEN ticks in the past
if (practical_offset > BUCKET_LEN)
head_offset += TICKS2DS(BUCKET_LEN)
practical_offset = 1
resumed = FALSE
+ // Check for when we have to reset buckets, typically from auto-reset
if ((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution))
reset_buckets()
bucket_list = src.bucket_list
resumed = FALSE
- if (!resumed)
- timer = null
-
- while (practical_offset <= BUCKET_LEN && head_offset + ((practical_offset-1)*world.tick_lag) <= world.time)
- var/datum/timedevent/head = bucket_list[practical_offset]
- if (!timer || !head || timer == head)
- head = bucket_list[practical_offset]
- timer = head
- while (timer)
+ // Iterate through each bucket starting from the practical offset
+ while (practical_offset <= BUCKET_LEN && head_offset + ((practical_offset - 1) * world.tick_lag) <= world.time)
+ var/datum/timedevent/timer
+ while ((timer = bucket_list[practical_offset]))
var/datum/callback/callBack = timer.callBack
if (!callBack)
- bucket_resolution = null //force bucket recreation
- CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
+ bucket_resolution = null // force bucket recreation
+ CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], \
+ head_offset: [head_offset], practical_offset: [practical_offset]")
+ timer.bucketEject() //pop the timer off of the bucket list.
+
+ // Invoke callback if possible
if (!timer.spent)
- spent += timer
timer.spent = world.time
callBack.InvokeAsync()
last_invoke_tick = world.time
- if (MC_TICK_CHECK)
- return
-
- timer = timer.next
- if (timer == head)
- break
-
-
- bucket_list[practical_offset++] = null
-
- //we freed up a bucket, lets see if anything in second_queue needs to be shifted to that bucket.
- var/i = 0
- var/L = length(second_queue)
- for (i in 1 to L)
- timer = second_queue[i]
- if (timer.timeToRun >= TIMER_MAX)
- i--
- break
-
- if (timer.timeToRun < head_offset)
- bucket_resolution = null //force bucket recreation
- stack_trace("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
-
- if (timer.callBack && !timer.spent)
- timer.callBack.InvokeAsync()
- spent += timer
- bucket_count++
- else if(!QDELETED(timer))
- qdel(timer)
- continue
-
- if (timer.timeToRun < head_offset + TICKS2DS(practical_offset-1))
- bucket_resolution = null //force bucket recreation
- stack_trace("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
- if (timer.callBack && !timer.spent)
- timer.callBack.InvokeAsync()
- spent += timer
- bucket_count++
- else if(!QDELETED(timer))
- qdel(timer)
- continue
-
- bucket_count++
- var/bucket_pos = max(1, BUCKET_POS(timer))
-
- var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
- if (!bucket_head)
- bucket_list[bucket_pos] = timer
- timer.next = null
- timer.prev = null
- continue
-
- if (!bucket_head.prev)
- bucket_head.prev = bucket_head
- timer.next = bucket_head
- timer.prev = bucket_head.prev
- timer.next.prev = timer
- timer.prev.next = timer
- if (i)
- second_queue.Cut(1, i+1)
-
- timer = null
-
- bucket_count -= length(spent)
-
- for (var/i in spent)
- var/datum/timedevent/qtimer = i
- if(QDELETED(qtimer))
- bucket_count++
- continue
- if(!(qtimer.flags & TIMER_LOOP))
- qdel(qtimer)
- else
- bucket_count++
- qtimer.spent = 0
- qtimer.bucketEject()
- if(qtimer.flags & TIMER_CLIENT_TIME)
- qtimer.timeToRun = REALTIMEOFDAY + qtimer.wait
+ if (timer.flags & TIMER_LOOP) // Prepare looping timers to re-enter the queue
+ timer.spent = 0
+ timer.timeToRun = world.time + timer.wait
+ timer.bucketJoin()
else
- qtimer.timeToRun = world.time + qtimer.wait
- qtimer.bucketJoin()
+ qdel(timer)
- spent.len = 0
+ if (MC_TICK_CHECK)
+ break
-//formated this way to be runtime resistant
+ if (!bucket_list[practical_offset])
+ // Empty the bucket, check if anything in the secondary queue should be shifted to this bucket
+ bucket_list[practical_offset++] = null
+ var/i = 0
+ for (i in 1 to length(second_queue))
+ timer = second_queue[i]
+ if (timer.timeToRun >= TIMER_MAX)
+ i--
+ break
+
+ // Check for timers that are scheduled to run in the past
+ if (timer.timeToRun < head_offset)
+ bucket_resolution = null // force bucket recreation
+ stack_trace("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. \
+ [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
+ break
+
+ // Check for timers that are not capable of being scheduled to run without rebuilding buckets
+ if (timer.timeToRun < head_offset + TICKS2DS(practical_offset - 1))
+ bucket_resolution = null // force bucket recreation
+ stack_trace("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to \
+ short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
+ break
+
+ timer.bucketJoin()
+ if (i)
+ second_queue.Cut(1, i+1)
+ if (MC_TICK_CHECK)
+ break
+
+/**
+ * Generates a string with details about the timed event for debugging purposes
+ */
/datum/controller/subsystem/timer/proc/get_timer_debug_string(datum/timedevent/TE)
. = "Timer: [TE]"
. += "Prev: [TE.prev ? TE.prev : "NULL"], Next: [TE.next ? TE.next : "NULL"]"
@@ -234,12 +221,16 @@ SUBSYSTEM_DEF(timer)
if(!TE.callBack)
. += ", NO CALLBACK"
+/**
+ * Destroys the existing buckets and creates new buckets from the existing timed events
+ */
/datum/controller/subsystem/timer/proc/reset_buckets()
- var/list/bucket_list = src.bucket_list
+ var/list/bucket_list = src.bucket_list // Store local reference to datum var, this is faster
var/list/alltimers = list()
- //collect the timers currently in the bucket
+
+ // Get all timers currently in the buckets
for (var/bucket_head in bucket_list)
- if (!bucket_head)
+ if (!bucket_head) // if bucket is empty for this tick
continue
var/datum/timedevent/bucket_node = bucket_head
do
@@ -247,25 +238,38 @@ SUBSYSTEM_DEF(timer)
bucket_node = bucket_node.next
while(bucket_node && bucket_node != bucket_head)
+ // Empty the list by zeroing and re-assigning the length
bucket_list.len = 0
bucket_list.len = BUCKET_LEN
+ // Reset values for the subsystem to their initial values
practical_offset = 1
bucket_count = 0
head_offset = world.time
bucket_resolution = world.tick_lag
+ // Add all timed events from the secondary queue as well
alltimers += second_queue
+
+ // If there are no timers being tracked by the subsystem,
+ // there is no need to do any further rebuilding
if (!length(alltimers))
return
+ // Sort all timers by time to run
sortTim(alltimers, .proc/cmp_timer)
+ // Get the earliest timer, and if the TTR is earlier than the current world.time,
+ // then set the head offset appropriately to be the earliest time tracked by the
+ // current set of buckets
var/datum/timedevent/head = alltimers[1]
-
if (head.timeToRun < head_offset)
head_offset = head.timeToRun
+ // Iterate through each timed event and insert it into an appropriate bucket,
+ // up unto the point that we can no longer insert into buckets as the TTR
+ // is outside the range we are tracking, then insert the remainder into the
+ // secondary queue
var/new_bucket_count
var/i = 1
for (i in 1 to length(alltimers))
@@ -273,34 +277,38 @@ SUBSYSTEM_DEF(timer)
if (!timer)
continue
- var/bucket_pos = BUCKET_POS(timer)
+ // Check that the TTR is within the range covered by buckets, when exceeded we've finished
if (timer.timeToRun >= TIMER_MAX)
i--
break
-
+ // Check that timer has a valid callback and hasn't been invoked
if (!timer.callBack || timer.spent)
- WARNING("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
+ WARNING("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], \
+ head_offset: [head_offset], practical_offset: [practical_offset]")
if (timer.callBack)
qdel(timer)
continue
+ // Insert the timer into the bucket, and perform necessary circular doubly-linked list operations
new_bucket_count++
+ var/bucket_pos = BUCKET_POS(timer)
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
if (!bucket_head)
bucket_list[bucket_pos] = timer
timer.next = null
timer.prev = null
continue
-
if (!bucket_head.prev)
bucket_head.prev = bucket_head
timer.next = bucket_head
timer.prev = bucket_head.prev
timer.next.prev = timer
timer.prev.next = timer
+
+ // Cut the timers that are tracked by the buckets from the secondary queue
if (i)
- alltimers.Cut(1, i+1)
+ alltimers.Cut(1, i + 1)
second_queue = alltimers
bucket_count = new_bucket_count
@@ -311,45 +319,64 @@ SUBSYSTEM_DEF(timer)
timer_id_dict |= SStimer.timer_id_dict
bucket_list |= SStimer.bucket_list
+/**
+ * # Timed Event
+ *
+ * This is the actual timer, it contains the callback and necessary data to maintain
+ * the timer.
+ *
+ * See the documentation for the timer subsystem for an explanation of the buckets referenced
+ * below in next and prev
+ */
/datum/timedevent
+ /// ID used for timers when the TIMER_STOPPABLE flag is present
var/id
+ /// The callback to invoke after the timer completes
var/datum/callback/callBack
+ /// The time at which the callback should be invoked at
var/timeToRun
+ /// The length of the timer
var/wait
+ /// Unique hash generated when TIMER_UNIQUE flag is present
var/hash
+ /// The source of the timedevent, whatever called addtimer
+ var/source
+ /// Flags associated with the timer, see _DEFINES/subsystems.dm
var/list/flags
- var/spent = 0 //time we ran the timer.
- var/name //for easy debugging.
- //cicular doublely linked list
+ /// Time at which the timer was invoked or destroyed
+ var/spent = 0
+ /// An informative name generated for the timer as its representation in strings, useful for debugging
+ var/name
+ /// Next timed event in the bucket
var/datum/timedevent/next
+ /// Previous timed event in the bucket
var/datum/timedevent/prev
-/datum/timedevent/New(datum/callback/callBack, wait, flags, hash)
+/datum/timedevent/New(datum/callback/callBack, wait, flags, hash, source)
var/static/nextid = 1
id = TIMER_ID_NULL
src.callBack = callBack
src.wait = wait
src.flags = flags
src.hash = hash
+ src.source = source
- if (flags & TIMER_CLIENT_TIME)
- timeToRun = REALTIMEOFDAY + wait
- else
- timeToRun = world.time + wait
+ // Determine time at which the timer's callback should be invoked
+ timeToRun = (flags & TIMER_CLIENT_TIME ? REALTIMEOFDAY : world.time) + wait
+ // Include the timer in the hash table if the timer is unique
if (flags & TIMER_UNIQUE)
SStimer.hashes[hash] = src
+ // Generate ID for the timer if the timer is stoppable, include in the timer id dictionary
if (flags & TIMER_STOPPABLE)
id = num2text(nextid, 100)
if (nextid >= SHORT_REAL_LIMIT)
- nextid += min(1, 2**round(nextid/SHORT_REAL_LIMIT))
+ nextid += min(1, 2 ** round(nextid / SHORT_REAL_LIMIT))
else
nextid++
SStimer.timer_id_dict[id] = src
- name = "Timer: [id] (\ref[src]), TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT", "TIMER_LOOP")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])"
-
if ((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME))
CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]")
@@ -390,23 +417,39 @@ SUBSYSTEM_DEF(timer)
prev = null
return QDEL_HINT_IWILLGC
+/**
+ * Removes this timed event from any relevant buckets, or the secondary queue
+ */
/datum/timedevent/proc/bucketEject()
+ // Attempt to find bucket that contains this timed event
var/bucketpos = BUCKET_POS(src)
+
+ // Store local references for the bucket list and secondary queue
+ // This is faster than referencing them from the datum itself
var/list/bucket_list = SStimer.bucket_list
var/list/second_queue = SStimer.second_queue
+
+ // Attempt to get the head of the bucket
var/datum/timedevent/buckethead
if(bucketpos > 0)
buckethead = bucket_list[bucketpos]
+
+ // Decrement the number of timers in buckets if the timed event is
+ // the head of the bucket, or has a TTR less than TIMER_MAX implying it fits
+ // into an existing bucket, or is otherwise not present in the secondary queue
if(buckethead == src)
bucket_list[bucketpos] = next
SStimer.bucket_count--
- else if(timeToRun < TIMER_MAX || next || prev)
+ else if(timeToRun < TIMER_MAX)
SStimer.bucket_count--
else
var/l = length(second_queue)
second_queue -= src
if(l == length(second_queue))
SStimer.bucket_count--
+
+ // Remove the timed event from the bucket, ensuring to maintain
+ // the integrity of the bucket's list if relevant
if(prev != next)
prev.next = next
next.prev = prev
@@ -415,32 +458,47 @@ SUBSYSTEM_DEF(timer)
next?.prev = null
prev = next = null
+/**
+ * Attempts to add this timed event to a bucket, will enter the secondary queue
+ * if there are no appropriate buckets at this time.
+ *
+ * Secondary queueing of timed events will occur when the timespan covered by the existing
+ * buckets is exceeded by the time at which this timed event is scheduled to be invoked.
+ * If the timed event is tracking client time, it will be added to a special bucket.
+ */
/datum/timedevent/proc/bucketJoin()
- var/list/L
+ // Generate debug-friendly name for timer
+ var/static/list/bitfield_flags = list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT", "TIMER_LOOP")
+ name = "Timer: [id] (\ref[src]), TTR: [timeToRun], wait:[wait] Flags: [jointext(bitfield2list(flags, bitfield_flags), ", ")], \
+ callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), \
+ callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""]), source: [source]"
+ // Check if this timed event should be diverted to the client time bucket, or the secondary queue
+ var/list/L
if (flags & TIMER_CLIENT_TIME)
L = SStimer.clienttime_timers
else if (timeToRun >= TIMER_MAX)
L = SStimer.second_queue
-
if(L)
- BINARY_INSERT(src, L, datum/timedevent, src, timeToRun, COMPARE_KEY)
+ BINARY_INSERT(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY)
return
- //get the list of buckets
+ // Get a local reference to the bucket list, this is faster than referencing the datum
var/list/bucket_list = SStimer.bucket_list
- //calculate our place in the bucket list
+ // Find the correct bucket for this timed event
var/bucket_pos = BUCKET_POS(src)
-
- //get the bucket for our tick
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
SStimer.bucket_count++
- //empty bucket, we will just add ourselves
+
+ // If there is no timed event at this position, then the bucket is 'empty'
+ // and we can just set this event to that position
if (!bucket_head)
bucket_list[bucket_pos] = src
return
- //other wise, lets do a simplified linked list add.
+
+ // Otherwise, we merely add this timed event into the bucket, which is a
+ // circularly doubly-linked list
if (!bucket_head.prev)
bucket_head.prev = bucket_head
next = bucket_head
@@ -448,7 +506,9 @@ SUBSYSTEM_DEF(timer)
next.prev = src
prev.next = src
-///Returns a string of the type of the callback for this timer
+/**
+ * Returns a string of the type of the callback for this timer
+ */
/datum/timedevent/proc/getcallingtype()
. = "ERROR"
if (callBack.object == GLOBAL_PROC)
@@ -457,14 +517,15 @@ SUBSYSTEM_DEF(timer)
. = "[callBack.object.type]"
/**
- * Create a new timer and insert it in the queue
- *
- * Arguments:
- * * callback the callback to call on timer finish
- * * wait deciseconds to run the timer for
- * * flags flags for this timer, see: code\__DEFINES\subsystems.dm
- */
-/proc/addtimer(datum/callback/callback, wait = 0, flags = 0)
+ * Create a new timer and insert it in the queue.
+ * You should not call this directly, and should instead use the addtimer macro, which includes source information.
+ *
+ * Arguments:
+ * * callback the callback to call on timer finish
+ * * wait deciseconds to run the timer for
+ * * flags flags for this timer, see: code\__DEFINES\subsystems.dm
+ */
+/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, file, line)
if (!callback)
CRASH("addtimer called without a callback")
@@ -472,31 +533,30 @@ SUBSYSTEM_DEF(timer)
stack_trace("addtimer called with a negative wait. Converting to [world.tick_lag]")
if (callback.object != GLOBAL_PROC && QDELETED(callback.object) && !QDESTROYING(callback.object))
- stack_trace("addtimer called with a callback assigned to a qdeleted object. In the future such timers will not be supported and may refuse to run or run with a 0 wait")
+ stack_trace("addtimer called with a callback assigned to a qdeleted object. In the future such timers will not \
+ be supported and may refuse to run or run with a 0 wait")
wait = max(CEILING(wait, world.tick_lag), world.tick_lag)
if(wait >= INFINITY)
CRASH("Attempted to create timer with INFINITY delay")
+ // Generate hash if relevant for timed events with the TIMER_UNIQUE flag
var/hash
-
if (flags & TIMER_UNIQUE)
- var/list/hashlist
- if(flags & TIMER_NO_HASH_WAIT)
- hashlist = list(callback.object, "([REF(callback.object)])", callback.delegate, flags & TIMER_CLIENT_TIME)
- else
- hashlist = list(callback.object, "([REF(callback.object)])", callback.delegate, wait, flags & TIMER_CLIENT_TIME)
+ var/list/hashlist = list(callback.object, "([REF(callback.object)])", callback.delegate, flags & TIMER_CLIENT_TIME)
+ if(!(flags & TIMER_NO_HASH_WAIT))
+ hashlist += wait
hashlist += callback.arguments
hash = hashlist.Join("|||||||")
var/datum/timedevent/hash_timer = SStimer.hashes[hash]
if(hash_timer)
- if (hash_timer.spent) //it's pending deletion, pretend it doesn't exist.
- hash_timer.hash = null //but keep it from accidentally deleting us
+ if (hash_timer.spent) // it's pending deletion, pretend it doesn't exist.
+ hash_timer.hash = null // but keep it from accidentally deleting us
else
if (flags & TIMER_OVERRIDE)
- hash_timer.hash = null //no need having it delete it's hash if we are going to replace it
+ hash_timer.hash = null // no need having it delete it's hash if we are going to replace it
qdel(hash_timer)
else
if (hash_timer.flags & TIMER_STOPPABLE)
@@ -505,24 +565,23 @@ SUBSYSTEM_DEF(timer)
else if(flags & TIMER_OVERRIDE)
stack_trace("TIMER_OVERRIDE used without TIMER_UNIQUE")
- var/datum/timedevent/timer = new(callback, wait, flags, hash)
+ var/datum/timedevent/timer = new(callback, wait, flags, hash, file && "[file]:[line]")
return timer.id
/**
- * Delete a timer
- *
- * Arguments:
- * * id a timerid or a /datum/timedevent
- */
+ * Delete a timer
+ *
+ * Arguments:
+ * * id a timerid or a /datum/timedevent
+ */
/proc/deltimer(id)
if (!id)
return FALSE
if (id == TIMER_ID_NULL)
CRASH("Tried to delete a null timerid. Use TIMER_STOPPABLE flag")
- if (!istext(id))
- if (istype(id, /datum/timedevent))
- qdel(id)
- return TRUE
+ if (istype(id, /datum/timedevent))
+ qdel(id)
+ return TRUE
//id is string
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
if (timer && !timer.spent)
@@ -531,25 +590,22 @@ SUBSYSTEM_DEF(timer)
return FALSE
/**
- * Get the remaining deciseconds on a timer
- *
- * Arguments:
- * * id a timerid or a /datum/timedevent
- */
+ * Get the remaining deciseconds on a timer
+ *
+ * Arguments:
+ * * id a timerid or a /datum/timedevent
+ */
/proc/timeleft(id)
if (!id)
return null
if (id == TIMER_ID_NULL)
CRASH("Tried to get timeleft of a null timerid. Use TIMER_STOPPABLE flag")
- if (!istext(id))
- if (istype(id, /datum/timedevent))
- var/datum/timedevent/timer = id
- return timer.timeToRun - world.time
+ if (istype(id, /datum/timedevent))
+ var/datum/timedevent/timer = id
+ return timer.timeToRun - world.time
//id is string
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
- if (timer && !timer.spent)
- return timer.timeToRun - world.time
- return null
+ return (timer && !timer.spent) ? timer.timeToRun - world.time : null
#undef BUCKET_LEN
#undef BUCKET_POS
diff --git a/code/controllers/subsystem/title.dm b/code/controllers/subsystem/title.dm
index bd843f959d..996f73ccf6 100644
--- a/code/controllers/subsystem/title.dm
+++ b/code/controllers/subsystem/title.dm
@@ -25,12 +25,12 @@ SUBSYSTEM_DEF(title)
SSmapping.HACK_LoadMapConfig()
for(var/S in provisional_title_screens)
var/list/L = splittext(S,"+")
- if((L.len == 1 && L[1] != "blank.png")|| (L.len > 1 && ((use_rare_screens && lowertext(L[1]) == "rare") || (lowertext(L[1]) == lowertext(SSmapping.config.map_name)))))
+ if((L.len == 1 && (L[1] != "exclude" && L[1] != "blank.png"))|| (L.len > 1 && ((use_rare_screens && lowertext(L[1]) == "rare") || (lowertext(L[1]) == lowertext(SSmapping.config.map_name)))))
title_screens += S
if(length(title_screens))
file_path = "[global.config.directory]/title_screens/images/[pick(title_screens)]"
-
+
if(!file_path)
file_path = "icons/default_title.dmi"
diff --git a/code/controllers/subsystem/vis_overlays.dm b/code/controllers/subsystem/vis_overlays.dm
index 0635709074..b0e5d6c689 100644
--- a/code/controllers/subsystem/vis_overlays.dm
+++ b/code/controllers/subsystem/vis_overlays.dm
@@ -5,10 +5,12 @@ SUBSYSTEM_DEF(vis_overlays)
init_order = INIT_ORDER_VIS
var/list/vis_overlay_cache
+ var/list/unique_vis_overlays
var/list/currentrun
/datum/controller/subsystem/vis_overlays/Initialize()
vis_overlay_cache = list()
+ unique_vis_overlays = list()
return ..()
/datum/controller/subsystem/vis_overlays/fire(resumed = FALSE)
@@ -29,31 +31,45 @@ SUBSYSTEM_DEF(vis_overlays)
return
//the "thing" var can be anything with vis_contents which includes images
-/datum/controller/subsystem/vis_overlays/proc/add_vis_overlay(atom/movable/thing, icon, iconstate, layer, plane, dir, alpha = 255, add_appearance_flags = NONE)
- . = "[icon]|[iconstate]|[layer]|[plane]|[dir]|[alpha]|[add_appearance_flags]"
- var/obj/effect/overlay/vis/overlay = vis_overlay_cache[.]
- if(!overlay)
- overlay = new
- overlay.icon = icon
- overlay.icon_state = iconstate
- overlay.layer = layer
- overlay.plane = plane
- overlay.dir = dir
- overlay.alpha = alpha
- overlay.appearance_flags |= add_appearance_flags
- vis_overlay_cache[.] = overlay
+/datum/controller/subsystem/vis_overlays/proc/add_vis_overlay(atom/movable/thing, icon, iconstate, layer, plane, dir, alpha = 255, add_appearance_flags = NONE, unique = FALSE)
+ var/obj/effect/overlay/vis/overlay
+ if(!unique)
+ . = "[icon]|[iconstate]|[layer]|[plane]|[dir]|[alpha]|[add_appearance_flags]"
+ overlay = vis_overlay_cache[.]
+ if(!overlay)
+ overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
+ vis_overlay_cache[.] = overlay
+ else
+ overlay.unused = 0
else
- overlay.unused = 0
+ overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
+ overlay.cache_expiration = -1
+ var/cache_id = "\ref[overlay]@{[world.time]}"
+ unique_vis_overlays += overlay
+ vis_overlay_cache[cache_id] = overlay
+ . = overlay
thing.vis_contents += overlay
if(!isatom(thing)) // Automatic rotation is not supported on non atoms
- return
+ return overlay
if(!thing.managed_vis_overlays)
thing.managed_vis_overlays = list(overlay)
- RegisterSignal(thing, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_vis_overlay)
else
thing.managed_vis_overlays += overlay
+ return overlay
+
+/datum/controller/subsystem/vis_overlays/proc/_create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
+ var/obj/effect/overlay/vis/overlay = new
+ overlay.icon = icon
+ overlay.icon_state = iconstate
+ overlay.layer = layer
+ overlay.plane = plane
+ overlay.dir = dir
+ overlay.alpha = alpha
+ overlay.appearance_flags |= add_appearance_flags
+ return overlay
+
/datum/controller/subsystem/vis_overlays/proc/remove_vis_overlay(atom/movable/thing, list/overlays)
thing.vis_contents -= overlays
@@ -62,15 +78,3 @@ SUBSYSTEM_DEF(vis_overlays)
thing.managed_vis_overlays -= overlays
if(!length(thing.managed_vis_overlays))
thing.managed_vis_overlays = null
- UnregisterSignal(thing, COMSIG_ATOM_DIR_CHANGE)
-
-/datum/controller/subsystem/vis_overlays/proc/rotate_vis_overlay(atom/thing, old_dir, new_dir)
- if(old_dir == new_dir)
- return
- var/rotation = dir2angle(old_dir) - dir2angle(new_dir)
- var/list/overlays_to_remove = list()
- for(var/i in thing.managed_vis_overlays)
- var/obj/effect/overlay/vis/overlay = i
- add_vis_overlay(thing, overlay.icon, overlay.icon_state, overlay.layer, overlay.plane, turn(overlay.dir, rotation), overlay.alpha, overlay.appearance_flags)
- overlays_to_remove += overlay
- remove_vis_overlay(thing, overlays_to_remove)
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index e5e1434ab8..f35389f171 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -92,7 +92,7 @@
trauma = _trauma
owner = trauma.owner
- setup_friend()
+ INVOKE_ASYNC(src, .proc/setup_friend)
join = new
join.Grant(src)
diff --git a/code/datums/cinematic.dm b/code/datums/cinematic.dm
index 7b3081cb33..2648ae1eab 100644
--- a/code/datums/cinematic.dm
+++ b/code/datums/cinematic.dm
@@ -30,7 +30,7 @@
/datum/cinematic
var/id = CINEMATIC_DEFAULT
var/list/watching = list() //List of clients watching this
- var/list/locked = list() //Who had mob_transforming set during the cinematic
+ var/list/locked = list() //Who had mob_transforming set during the cinematic
var/is_global = FALSE //Global cinematics will override mob-specific ones
var/obj/screen/cinematic/screen
var/datum/callback/special_callback //For special effects synced with animation (explosions after the countdown etc)
@@ -45,7 +45,7 @@
if(!CC)
continue
var/client/C = CC
- //C.mob.clear_fullscreen("cinematic")
+ C.mob.clear_fullscreen("cinematic")
C.screen -= screen
watching = null
QDEL_NULL(screen)
@@ -54,7 +54,7 @@
if(!MM)
continue
var/mob/M = MM
- M.mob_transforming = FALSE
+ M.mob_transforming = FALSE
locked = null
return ..()
@@ -93,7 +93,7 @@
toggle_ooc(TRUE)
/datum/cinematic/proc/show_to(mob/M, client/C)
- //SIGNAL_HANDLER //must not wait.
+ SIGNAL_HANDLER
if(!M.mob_transforming)
locked += M
@@ -101,7 +101,7 @@
if(!C)
return
watching += C
- //M.overlay_fullscreen("cinematic",/obj/screen/fullscreen/cinematic_backdrop)
+ M.overlay_fullscreen("cinematic",/obj/screen/fullscreen/cinematic_backdrop)
C.screen += screen
//Sound helper
@@ -122,7 +122,7 @@
sleep(50)
/datum/cinematic/proc/replacement_cinematic(datum/source, datum/cinematic/other)
- //SIGNAL_HANDLER
+ SIGNAL_HANDLER
if(!is_global && other.is_global) //Allow it to play if we're local and it's global
return NONE
@@ -210,6 +210,20 @@
special()
screen.icon_state = "summary_cult"
+// /datum/cinematic/cult_fail
+// id = CINEMATIC_CULT_FAIL
+
+// /datum/cinematic/cult_fail/content()
+// screen.icon_state = "station_intact"
+// sleep(20)
+// cinematic_sound(sound('sound/creatures/narsie_rises.ogg'))
+// sleep(60)
+// cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+// sleep(10)
+// cinematic_sound(sound('sound/magic/demon_dies.ogg'))
+// sleep(30)
+// special()
+
/datum/cinematic/nuke_annihilation
id = CINEMATIC_ANNIHILATION
diff --git a/code/datums/components/combat_mode.dm b/code/datums/components/combat_mode.dm
index b9952e9133..29d90fd509 100644
--- a/code/datums/components/combat_mode.dm
+++ b/code/datums/components/combat_mode.dm
@@ -125,7 +125,7 @@
///Changes the user direction to (try) keep match the pointer.
/datum/component/combat_mode/proc/on_move(atom/movable/source, dir, atom/oldloc, forced)
var/mob/living/L = source
- if(mode_flags & COMBAT_MODE_ACTIVE && L.client && lastmousedir && lastmousedir != dir)
+ if((mode_flags & COMBAT_MODE_ACTIVE) && L.client)
L.setDir(lastmousedir, ismousemovement = TRUE)
/// Added movement delay if moving backward.
diff --git a/code/datums/components/footstep.dm b/code/datums/components/footstep.dm
index f767c607a5..8b326ac424 100644
--- a/code/datums/components/footstep.dm
+++ b/code/datums/components/footstep.dm
@@ -46,7 +46,7 @@
var/mob/living/LM = parent
if(!T.footstep || LM.buckled || !CHECK_MOBILITY(LM, MOBILITY_STAND) || LM.buckled || LM.throwing || (LM.movement_type & (VENTCRAWLING | FLYING)))
if (LM.lying && !LM.buckled && !(!T.footstep || LM.movement_type & (VENTCRAWLING | FLYING))) //play crawling sound if we're lying
- playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * volume)
+ playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * volume, falloff_distance = 1)
return
if(HAS_TRAIT(LM, TRAIT_SILENT_STEP))
@@ -75,7 +75,7 @@
if(!T)
return
if(isfile(footstep_sounds) || istext(footstep_sounds))
- playsound(T, footstep_sounds, volume)
+ playsound(T, footstep_sounds, volume, falloff_distance = 1)
return
var/turf_footstep
switch(footstep_type)
@@ -89,7 +89,7 @@
turf_footstep = T.footstep
if(!turf_footstep)
return
- playsound(T, pick(footstep_sounds[turf_footstep][1]), footstep_sounds[turf_footstep][2] * volume, TRUE, footstep_sounds[turf_footstep][3] + e_range)
+ playsound(T, pick(footstep_sounds[turf_footstep][1]), footstep_sounds[turf_footstep][2] * volume, TRUE, footstep_sounds[turf_footstep][3] + e_range, falloff_distance = 1)
/datum/component/footstep/proc/play_humanstep()
var/turf/open/T = prepare_step()
@@ -114,10 +114,10 @@
turf_footstep = T.footstep
L = GLOB.footstep
if(FOOTSTEP_MOB_SLIME)
- playsound(T, 'sound/effects/footstep/slime1.ogg', 50 * volume)
+ playsound(T, 'sound/effects/footstep/slime1.ogg', 50 * volume, falloff_distance = 1)
return
if(FOOTSTEP_MOB_CRAWL)
- playsound(T, 'sound/effects/footstep/crawl1.ogg', 50 * volume)
+ playsound(T, 'sound/effects/footstep/crawl1.ogg', 50 * volume, falloff_distance = 1)
return
special = TRUE
else
@@ -126,13 +126,13 @@
playsound(T, pick(GLOB.footstep[T.footstep][1]),
GLOB.footstep[T.footstep][2] * volume,
TRUE,
- GLOB.footstep[T.footstep][3] + e_range)
+ GLOB.footstep[T.footstep][3] + e_range, falloff_distance = 1)
return
if(!special && H.dna.species.special_step_sounds)
- playsound(T, pick(H.dna.species.special_step_sounds), 50, TRUE)
+ playsound(T, pick(H.dna.species.special_step_sounds), 50, TRUE, falloff_distance = 1)
else
playsound(T, pick(L[turf_footstep][1]),
L[turf_footstep][2] * volume,
TRUE,
- L[turf_footstep][3] + e_range)
+ L[turf_footstep][3] + e_range, falloff_distance = 1)
diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm
index c2b3ad1f30..61098c8b66 100644
--- a/code/datums/components/gps.dm
+++ b/code/datums/components/gps.dm
@@ -21,7 +21,7 @@ GLOBAL_LIST_EMPTY(GPS_list)
var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user.
var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown
-/datum/component/gps/item/Initialize(_gpstag = "COM0", emp_proof = FALSE)
+/datum/component/gps/item/Initialize(_gpstag = "COM0", emp_proof = FALSE, starton = TRUE)
. = ..()
if(. == COMPONENT_INCOMPATIBLE || !isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -33,6 +33,8 @@ GLOBAL_LIST_EMPTY(GPS_list)
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp_act)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
RegisterSignal(parent, COMSIG_CLICK_ALT, .proc/on_AltClick)
+ if(!starton)
+ tracking = FALSE
///Called on COMSIG_ITEM_ATTACK_SELF
/datum/component/gps/item/proc/interact(datum/source, mob/user)
diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm
index 03f3820a9b..e9aa5afe92 100644
--- a/code/datums/components/riding.dm
+++ b/code/datums/components/riding.dm
@@ -36,15 +36,15 @@
if(del_on_unbuckle_all && !AM.has_buckled_mobs())
qdel(src)
-/datum/component/riding/proc/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
+/datum/component/riding/proc/vehicle_mob_buckle(datum/source, mob/living/M, force)
handle_vehicle_offsets()
-/datum/component/riding/proc/handle_vehicle_layer()
+/datum/component/riding/proc/handle_vehicle_layer(dir)
var/atom/movable/AM = parent
var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER)
- . = defaults["[AM.dir]"]
- if(directional_vehicle_layers["[AM.dir]"])
- . = directional_vehicle_layers["[AM.dir]"]
+ . = defaults["[dir]"]
+ if(directional_vehicle_layers["[dir]"])
+ . = directional_vehicle_layers["[dir]"]
if(isnull(.)) //you can set it to null to not change it.
. = AM.layer
AM.layer = .
@@ -52,12 +52,17 @@
/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
directional_vehicle_layers["[dir]"] = layer
-/datum/component/riding/proc/vehicle_moved(datum/source)
+/datum/component/riding/proc/vehicle_moved(datum/source, oldLoc, dir)
+ SIGNAL_HANDLER
+
var/atom/movable/AM = parent
+ if (isnull(dir))
+ dir = AM.dir
+ AM.set_glide_size(DELAY_TO_GLIDE_SIZE(vehicle_move_delay), FALSE)
for(var/i in AM.buckled_mobs)
ride_check(i)
- handle_vehicle_offsets()
- handle_vehicle_layer()
+ handle_vehicle_offsets(dir)
+ handle_vehicle_layer(dir)
/datum/component/riding/proc/ride_check(mob/living/M)
var/atom/movable/AM = parent
@@ -74,9 +79,9 @@
/datum/component/riding/proc/additional_offset_checks()
return TRUE
-/datum/component/riding/proc/handle_vehicle_offsets()
+/datum/component/riding/proc/handle_vehicle_offsets(dir)
var/atom/movable/AM = parent
- var/AM_dir = "[AM.dir]"
+ var/AM_dir = "[dir]"
var/passindex = 0
if(AM.has_buckled_mobs())
for(var/m in AM.buckled_mobs)
@@ -177,8 +182,8 @@
else
last_move_diagonal = FALSE
- handle_vehicle_offsets()
- handle_vehicle_layer()
+ handle_vehicle_offsets(direction)
+ handle_vehicle_layer(direction)
else
to_chat(user, "You'll need the keys in one of your hands to [drive_verb] [AM].")
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index 0552a791ea..a285b7d3f2 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -19,7 +19,14 @@
/// chance we'll be stopped from squeaking by cooldown when something crossing us squeaks
var/cross_squeak_delay_chance = 33 // about 3 things can squeak at a time
-/datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override)
+ ///extra-range for this component's sound
+ var/sound_extra_range = -1
+ ///when sounds start falling off for the squeak
+ var/sound_falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE
+ ///sound exponent for squeak. Defaults to 10 as squeaking is loud and annoying enough.
+ var/sound_falloff_exponent = 10
+
+/datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override, extrarange, falloff_exponent, fallof_distance)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY), .proc/play_squeak)
@@ -45,6 +52,12 @@
step_delay = step_delay_override
if(isnum(use_delay_override))
use_delay = use_delay_override
+ if(isnum(extrarange))
+ sound_extra_range = extrarange
+ if(isnum(falloff_exponent))
+ sound_falloff_exponent = falloff_exponent
+ if(isnum(fallof_distance))
+ sound_falloff_distance = fallof_distance
/datum/component/squeak/UnregisterFromParent()
if(!isatom(parent))
@@ -62,6 +75,7 @@
return ..()
/datum/component/squeak/proc/play_squeak()
+ SIGNAL_HANDLER
do_play_squeak()
/datum/component/squeak/proc/do_play_squeak(bypass_cooldown = FALSE)
@@ -69,14 +83,16 @@
return FALSE
if(prob(squeak_chance))
if(!override_squeak_sounds)
- playsound(parent, pickweight(default_squeak_sounds), volume, 1, -1)
+ playsound(parent, pickweight(default_squeak_sounds), volume, TRUE, sound_extra_range, sound_falloff_exponent, falloff_distance = sound_falloff_distance)
else
- playsound(parent, pickweight(override_squeak_sounds), volume, 1, -1)
+ playsound(parent, pickweight(override_squeak_sounds), volume, TRUE, sound_extra_range, sound_falloff_exponent, falloff_distance = sound_falloff_distance)
last_squeak = world.time
return TRUE
return FALSE
/datum/component/squeak/proc/step_squeak()
+ SIGNAL_HANDLER
+
if(steps > step_delay)
do_play_squeak(TRUE)
steps = 0
@@ -84,20 +100,22 @@
steps++
/datum/component/squeak/proc/play_squeak_crossed(datum/source, atom/movable/AM)
+ SIGNAL_HANDLER
+
if(isitem(AM))
var/obj/item/I = AM
if(I.item_flags & ABSTRACT)
return
- else if(istype(AM, /obj/item/projectile))
- var/obj/item/projectile/P = AM
- if(P.original != parent)
- return
+ if(AM.movement_type & (FLYING|FLOATING) || !AM.has_gravity())
+ return
var/atom/current_parent = parent
if(isturf(current_parent.loc))
if(do_play_squeak())
SEND_SIGNAL(AM, COMSIG_CROSS_SQUEAKED)
/datum/component/squeak/proc/use_squeak()
+ SIGNAL_HANDLER
+
if(last_use + use_delay < world.time)
last_use = world.time
play_squeak()
@@ -118,6 +136,8 @@
RegisterSignal(holder, COMSIG_ATOM_DIR_CHANGE, .proc/holder_dir_change)
/datum/component/squeak/proc/holder_dir_change(datum/source, old_dir, new_dir)
+ SIGNAL_HANDLER
+
//If the dir changes it means we're going through a bend in the pipes, let's pretend we bumped the wall
if(old_dir != new_dir)
play_squeak()
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index f93d40bb04..2fa5a20d7a 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -449,6 +449,10 @@
// this must come before the screen objects only block, dunno why it wasn't before
if(over_object == M)
user_show_to_mob(M)
+ return
+ if(isrevenant(M))
+ RevenantThrow(over_object, M, source)
+ return
if(!M.incapacitated())
if(!istype(over_object, /obj/screen))
dump_content_at(over_object, M)
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index c69df4f5e3..10ff5bda3d 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -85,6 +85,10 @@
to_chat(user, "You're not ready to tackle!")
return
+ if(!user.mob_has_gravity() ||!user.loc.has_gravity() || isspaceturf(user.loc))
+ to_chat(user, "You can't find your footing without gravity!")
+ return
+
if(user.has_status_effect(STATUS_EFFECT_TASED)) // can't tackle if you just got tased
to_chat(user, "You can't tackle while tased!")
return
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index d11532a883..42580425ce 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -77,21 +77,21 @@
/**
- * Default implementation of clean-up code.
- *
- * This should be overridden to remove all references pointing to the object being destroyed, if
- * you do override it, make sure to call the parent and return it's return value by default
- *
- * Return an appropriate [QDEL_HINT][QDEL_HINT_QUEUE] to modify handling of your deletion;
- * in most cases this is [QDEL_HINT_QUEUE].
- *
- * The base case is responsible for doing the following
- * * Erasing timers pointing to this datum
- * * Erasing compenents on this datum
- * * Notifying datums listening to signals from this datum that we are going away
- *
- * Returns [QDEL_HINT_QUEUE]
- */
+ * Default implementation of clean-up code.
+ *
+ * This should be overridden to remove all references pointing to the object being destroyed, if
+ * you do override it, make sure to call the parent and return it's return value by default
+ *
+ * Return an appropriate [QDEL_HINT][QDEL_HINT_QUEUE] to modify handling of your deletion;
+ * in most cases this is [QDEL_HINT_QUEUE].
+ *
+ * The base case is responsible for doing the following
+ * * Erasing timers pointing to this datum
+ * * Erasing compenents on this datum
+ * * Notifying datums listening to signals from this datum that we are going away
+ *
+ * Returns [QDEL_HINT_QUEUE]
+ */
/datum/proc/Destroy(force=FALSE, ...)
SHOULD_CALL_PARENT(TRUE)
tag = null
@@ -138,8 +138,6 @@
UnregisterSignal(target, signal_procs[target])
//END: ECS SHIT
- SSsounds.free_datum_channels(src) //?? (not on tg)
-
return QDEL_HINT_QUEUE
#ifdef DATUMVAR_DEBUGGING_MODE
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index a91549ab4c..0bb803ddb3 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -4,7 +4,7 @@
/datum/proc/can_vv_get(var_name)
return TRUE
-/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited
+/datum/proc/vv_edit_var(var_name, var_value, massedit) //called whenever a var is edited
if(var_name == NAMEOF(src, vars))
return FALSE
vars[var_name] = var_value
diff --git a/code/datums/diseases/heart_failure.dm b/code/datums/diseases/heart_failure.dm
index aabb9ed144..952ce4f18d 100644
--- a/code/datums/diseases/heart_failure.dm
+++ b/code/datums/diseases/heart_failure.dm
@@ -37,7 +37,7 @@
to_chat(H, "You feel [pick("full", "nauseated", "sweaty", "weak", "tired", "short on breath", "uneasy")].")
if(3 to 4)
if(!sound)
- H.playsound_local(H, 'sound/health/slowbeat.ogg',40,0, channel = CHANNEL_HEARTBEAT)
+ H.playsound_local(H, 'sound/health/slowbeat.ogg', 40, FALSE, channel = CHANNEL_HEARTBEAT)
sound = TRUE
if(prob(3))
to_chat(H, "You feel a sharp pain in your chest!")
@@ -53,7 +53,7 @@
H.emote("cough")
if(5)
H.stop_sound_channel(CHANNEL_HEARTBEAT)
- H.playsound_local(H, 'sound/effects/singlebeat.ogg', 100, 0)
+ H.playsound_local(H, 'sound/effects/singlebeat.ogg', 100, FALSE)
if(H.stat == CONSCIOUS)
H.visible_message("[H] clutches at [H.p_their()] chest as if [H.p_their()] heart is stopping!")
H.adjustStaminaLoss(60)
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index 44775ed031..8dca59ea94 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -121,7 +121,7 @@
add_monkey(affected_mob.mind)
if(ishuman(affected_mob))
var/mob/living/carbon/monkey/M = affected_mob.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
- M.ventcrawler = VENTCRAWLER_ALWAYS
+ M.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
/datum/disease/transformation/jungle_fever/stage_act()
diff --git a/code/datums/elements/bed_tucking.dm b/code/datums/elements/bed_tucking.dm
index 602c93fab3..4a498b2ed8 100644
--- a/code/datums/elements/bed_tucking.dm
+++ b/code/datums/elements/bed_tucking.dm
@@ -57,4 +57,4 @@
tucked.transform = turn(tucked.transform, -rotation_degree)
- UnregisterSignal(tucked, COMSIG_ITEM_PICKUP)
\ No newline at end of file
+ UnregisterSignal(tucked, COMSIG_ITEM_PICKUP)
diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm
index d770e0f30d..619f674969 100644
--- a/code/datums/elements/mob_holder.dm
+++ b/code/datums/elements/mob_holder.dm
@@ -141,7 +141,7 @@
/obj/item/clothing/head/mob_holder/dropped(mob/user)
. = ..()
- if(held_mob && isturf(loc))//don't release on soft-drops
+ if(held_mob && !ismob(loc))//don't release on soft-drops
release()
/obj/item/clothing/head/mob_holder/proc/release()
diff --git a/code/datums/elements/ventcrawling.dm b/code/datums/elements/ventcrawling.dm
new file mode 100644
index 0000000000..254345a97f
--- /dev/null
+++ b/code/datums/elements/ventcrawling.dm
@@ -0,0 +1,36 @@
+/datum/element/ventcrawling
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
+ id_arg_index = 2
+ var/tier
+
+/datum/element/ventcrawling/Attach(datum/target, duration = 0, given_tier = VENTCRAWLER_NUDE)
+ . = ..()
+
+ var/mob/living/person = target
+ if(!istype(person))
+ return FALSE
+
+ src.tier = given_tier
+
+ RegisterSignal(target, COMSIG_HANDLE_VENTCRAWL, .proc/handle_ventcrawl)
+ RegisterSignal(target, COMSIG_CHECK_VENTCRAWL, .proc/check_ventcrawl)
+ to_chat(target, "You can ventcrawl! Use alt+click on vents to quickly travel about the station.")
+
+ if(duration!=0)
+ addtimer(CALLBACK(src, .proc/Detach, target), duration)
+
+/datum/element/ventcrawling/Detach(datum/target)
+ UnregisterSignal(target, list(COMSIG_HANDLE_VENTCRAWL, COMSIG_CHECK_VENTCRAWL))
+ to_chat(target, "You can no longer ventcrawl.")
+
+ return ..()
+
+/datum/element/ventcrawling/proc/handle_ventcrawl(datum/target,atom/A)
+ var/mob/living/person = target
+ if(!istype(person))
+ return FALSE
+
+ person.handle_ventcrawl(A,tier)
+
+/datum/element/ventcrawling/proc/check_ventcrawl()
+ return tier
diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm
index ca65186063..9a29158b33 100644
--- a/code/datums/explosion.dm
+++ b/code/datums/explosion.dm
@@ -33,6 +33,14 @@ GLOBAL_LIST_EMPTY(explosions)
EX_PREPROCESS_EXIT_CHECK\
}
+#define CREAK_DELAY 5 SECONDS //Time taken for the creak to play after explosion, if applicable.
+#define FAR_UPPER 60 //Upper limit for the far_volume, distance, clamped.
+#define FAR_LOWER 40 //lower limit for the far_volume, distance, clamped.
+#define PROB_SOUND 75 //The probability modifier for a sound to be an echo, or a far sound. (0-100)
+#define SHAKE_CLAMP 2.5 //The limit for how much the camera can shake for out of view booms.
+#define FREQ_UPPER 40 //The upper limit for the randomly selected frequency.
+#define FREQ_LOWER 25 //The lower of the above.
+
/datum/explosion/New(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke)
set waitfor = FALSE
@@ -89,7 +97,7 @@ GLOBAL_LIST_EMPTY(explosions)
if(adminlog)
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in [ADMIN_VERBOSEJMP(epicenter)]")
log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in [loc_name(epicenter)]")
-
+
deadchat_broadcast("An explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) has occured at ([get_area(epicenter)])", turf_target = get_turf(epicenter))
var/x0 = epicenter.x
@@ -115,13 +123,14 @@ GLOBAL_LIST_EMPTY(explosions)
var/sound/creaking_explosion_sound = sound(get_sfx("explosion_creaking"))
var/sound/hull_creaking_sound = sound(get_sfx("hull_creaking"))
var/sound/explosion_echo_sound = sound('sound/effects/explosion_distant.ogg')
- var/on_station = SSmapping.level_trait(epicenter.z, ZTRAIT_STATION)
+ var/on_station = SSmapping.level_trait(epicenter.z, ZTRAIT_STATION)
var/creaking_explosion = FALSE
if(prob(devastation_range*30+heavy_impact_range*5) && on_station) // Huge explosions are near guaranteed to make the station creak and whine, smaller ones might.
creaking_explosion = TRUE // prob over 100 always returns true
- for(var/mob/M in GLOB.player_list)
+ for(var/MN in GLOB.player_list)
+ var/mob/M = MN
// Double check for client
var/turf/M_turf = get_turf(M)
if(M_turf && M_turf.z == z0)
@@ -131,15 +140,15 @@ GLOBAL_LIST_EMPTY(explosions)
baseshakeamount = sqrt((orig_max_distance - dist)*0.1)
// If inside the blast radius + world.view - 2
if(dist <= round(max_range + world.view - 2, 1))
- M.playsound_local(epicenter, null, 100, 1, frequency, falloff = 5, S = explosion_sound)
+ M.playsound_local(epicenter, null, 100, 1, frequency, S = explosion_sound)
if(baseshakeamount > 0)
shake_camera(M, 25, clamp(baseshakeamount, 0, 10))
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
else if(dist <= far_dist)
- var/far_volume = clamp(far_dist/2, 40, 60) // Volume is based on explosion size and dist
+ var/far_volume = clamp(far_dist/2, FAR_LOWER, FAR_UPPER) // Volume is based on explosion size and dist
if(creaking_explosion)
M.playsound_local(epicenter, null, far_volume, 1, frequency, S = creaking_explosion_sound, distance_multiplier = 0)
- else if(prob(75))
+ else if(prob(PROB_SOUND)) // Sound variety during meteor storm/tesloose/other bad event
M.playsound_local(epicenter, null, far_volume, 1, frequency, S = far_explosion_sound, distance_multiplier = 0) // Far sound
else
M.playsound_local(epicenter, null, far_volume, 1, frequency, S = explosion_echo_sound, distance_multiplier = 0) // Echo sound
@@ -147,18 +156,18 @@ GLOBAL_LIST_EMPTY(explosions)
if(baseshakeamount > 0 || devastation_range)
if(!baseshakeamount) // Devastating explosions rock the station and ground
baseshakeamount = devastation_range*3
- shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5))
-
- else if(M.can_hear() && !isspaceturf(get_turf(M)) && heavy_impact_range) // Big enough explosions echo throughout the hull
+ shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, SHAKE_CLAMP))
+ else if(!isspaceturf(get_turf(M)) && heavy_impact_range) // Big enough explosions echo throughout the hull
var/echo_volume = 40
if(devastation_range)
baseshakeamount = devastation_range
- shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5))
+ shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, SHAKE_CLAMP))
echo_volume = 60
M.playsound_local(epicenter, null, echo_volume, 1, frequency, S = explosion_echo_sound, distance_multiplier = 0)
if(creaking_explosion) // 5 seconds after the bang, the station begins to creak
- addtimer(CALLBACK(M, /mob/proc/playsound_local, epicenter, null, rand(25, 40), 1, frequency, null, null, FALSE, hull_creaking_sound, null, null, null, null, 0), 5 SECONDS)
+ addtimer(CALLBACK(M, /mob/proc/playsound_local, epicenter, null, rand(FREQ_LOWER, FREQ_UPPER), 1, frequency, null, null, FALSE, hull_creaking_sound, 0), CREAK_DELAY)
+
EX_PREPROCESS_CHECK_TICK
//postpone processing for a bit
@@ -230,8 +239,13 @@ GLOBAL_LIST_EMPTY(explosions)
atoms += A
for(var/i in atoms)
var/atom/A = i
- if(!QDELETED(A))
- A.ex_act(dist)
+ if(QDELETED(A))
+ continue
+ A.ex_act(dist, null, src)
+ if(QDELETED(A) || !ismovable(A))
+ continue
+ var/atom/movable/AM = A
+ LAZYADD(AM.acted_explosions, explosion_id)
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
new /obj/effect/hotspot(T) //Mostly for ambience!
@@ -316,6 +330,14 @@ GLOBAL_LIST_EMPTY(explosions)
++stopped
qdel(src)
+#undef CREAK_DELAY
+#undef FAR_UPPER
+#undef FAR_LOWER
+#undef PROB_SOUND
+#undef SHAKE_CLAMP
+#undef FREQ_UPPER
+#undef FREQ_LOWER
+
#undef EX_PREPROCESS_EXIT_CHECK
#undef EX_PREPROCESS_CHECK_TICK
diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm
index 4bf02e8587..c54f6c971a 100644
--- a/code/datums/holocall.dm
+++ b/code/datums/holocall.dm
@@ -237,7 +237,7 @@
/obj/item/disk/holodisk/Initialize(mapload)
. = ..()
if(preset_record_text)
- build_record()
+ INVOKE_ASYNC(src, .proc/build_record)
/obj/item/disk/holodisk/Destroy()
QDEL_NULL(record)
@@ -425,42 +425,42 @@
"}
/obj/item/disk/holodisk/ruin/snowengieruin
- name = "Blackbox Print-out #EB412"
- desc = "A holodisk containing the last moments of EB412. There's a bloody fingerprint on it."
- preset_image_type = /datum/preset_holoimage/engineer
- preset_record_text = {"
- NAME Dave Tundrale
- SAY Maria, how's Build?
- DELAY 10
- NAME Maria Dell
- PRESET /datum/preset_holoimage/engineer/atmos
- SAY It's fine, don't worry. I've got Plastic on it. And frankly, i'm kinda busy with, the, uhhm, incinerator.
- DELAY 30
- NAME Dave Tundrale
- PRESET /datum/preset_holoimage/engineer
- SAY Aight, wonderful. The science mans been kinda shit though. No RCDs-
- DELAY 20
- NAME Maria Dell
- PRESET /datum/preset_holoimage/engineer/atmos
- SAY Enough about your RCDs. They're not even that important, just bui-
- DELAY 15
- SOUND explosion
- DELAY 10
- SAY Oh, shit!
- DELAY 10
- PRESET /datum/preset_holoimage/engineer/atmos/rig
- LANGUAGE /datum/language/narsie
- NAME Unknown
- SAY RISE, MY LORD!!
- DELAY 10
- LANGUAGE /datum/language/common
- NAME Plastic
- PRESET /datum/preset_holoimage/engineer/rig
- SAY Fuck, fuck, fuck!
- DELAY 20
- SAY It's loose! CALL THE FUCKING SHUTT-
- DELAY 10
- PRESET /datum/preset_holoimage/corgi
- NAME Blackbox Automated Message
- SAY Connection lost. Dumping audio logs to disk.
- DELAY 50"}
+ name = "Blackbox Print-out #EB412"
+ desc = "A holodisk containing the last moments of EB412. There's a bloody fingerprint on it."
+ preset_image_type = /datum/preset_holoimage/engineer
+ preset_record_text = {"
+ NAME Dave Tundrale
+ SAY Maria, how's Build?
+ DELAY 10
+ NAME Maria Dell
+ PRESET /datum/preset_holoimage/engineer/atmos
+ SAY It's fine, don't worry. I've got Plastic on it. And frankly, i'm kinda busy with, the, uhhm, incinerator.
+ DELAY 30
+ NAME Dave Tundrale
+ PRESET /datum/preset_holoimage/engineer
+ SAY Aight, wonderful. The science mans been kinda shit though. No RCDs-
+ DELAY 20
+ NAME Maria Dell
+ PRESET /datum/preset_holoimage/engineer/atmos
+ SAY Enough about your RCDs. They're not even that important, just bui-
+ DELAY 15
+ SOUND explosion
+ DELAY 10
+ SAY Oh, shit!
+ DELAY 10
+ PRESET /datum/preset_holoimage/engineer/atmos/rig
+ LANGUAGE /datum/language/narsie
+ NAME Unknown
+ SAY RISE, MY LORD!!
+ DELAY 10
+ LANGUAGE /datum/language/common
+ NAME Plastic
+ PRESET /datum/preset_holoimage/engineer/rig
+ SAY Fuck, fuck, fuck!
+ DELAY 20
+ SAY It's loose! CALL THE FUCKING SHUTT-
+ DELAY 10
+ PRESET /datum/preset_holoimage/corgi
+ NAME Blackbox Automated Message
+ SAY Connection lost. Dumping audio logs to disk.
+ DELAY 50"}
diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm
index 8bee4f3d1c..6af3b3c993 100644
--- a/code/datums/looping_sounds/_looping_sound.dm
+++ b/code/datums/looping_sounds/_looping_sound.dm
@@ -18,8 +18,12 @@
var/list/atom/output_atoms
var/mid_sounds
var/mid_length
+ ///Override for volume of start sound
+ var/start_volume
var/start_sound
var/start_length
+ ///Override for volume of end sound
+ var/end_volume
var/end_sound
var/chance
var/volume = 100
@@ -27,10 +31,9 @@
var/max_loops
var/direct
var/extra_range = 0
- var/falloff
-
+ var/falloff_exponent
var/timerid
- var/init_timerid
+ var/falloff_distance
/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, _direct=FALSE)
if(!mid_sounds)
@@ -51,16 +54,13 @@
/datum/looping_sound/proc/start(atom/add_thing)
if(add_thing)
output_atoms |= add_thing
- if(timerid || init_timerid)
+ if(timerid)
return
on_start()
/datum/looping_sound/proc/stop(atom/remove_thing)
if(remove_thing)
output_atoms -= remove_thing
- if(init_timerid)
- deltimer(init_timerid)
- init_timerid = null
if(!timerid)
return
on_stop()
@@ -76,18 +76,18 @@
if(!timerid)
timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP)
-/datum/looping_sound/proc/play(soundfile)
+/datum/looping_sound/proc/play(soundfile, volume_override)
var/list/atoms_cache = output_atoms
var/sound/S = sound(soundfile)
if(direct)
S.channel = SSsounds.random_available_channel()
- S.volume = volume
+ S.volume = volume_override || volume //Use volume as fallback if theres no override
for(var/i in 1 to atoms_cache.len)
var/atom/thing = atoms_cache[i]
if(direct)
SEND_SOUND(thing, S)
else
- playsound(thing, S, volume, vary, extra_range, falloff)
+ playsound(thing, S, volume, vary, extra_range, falloff_exponent = falloff_exponent, falloff_distance = falloff_distance)
/datum/looping_sound/proc/get_sound(starttime, _mid_sounds)
. = _mid_sounds || mid_sounds
@@ -97,10 +97,10 @@
/datum/looping_sound/proc/on_start()
var/start_wait = 0
if(start_sound)
- play(start_sound)
+ play(start_sound, start_volume)
start_wait = start_length
- init_timerid = addtimer(CALLBACK(src, .proc/sound_loop), start_wait, TIMER_CLIENT_TIME | TIMER_STOPPABLE)
+ addtimer(CALLBACK(src, .proc/sound_loop), start_wait, TIMER_CLIENT_TIME)
/datum/looping_sound/proc/on_stop()
if(end_sound)
- play(end_sound)
+ play(end_sound, end_volume)
diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm
index 4f6996bfdd..f7a3b46118 100644
--- a/code/datums/looping_sounds/machinery_sounds.dm
+++ b/code/datums/looping_sounds/machinery_sounds.dm
@@ -4,7 +4,7 @@
mid_sounds = list('sound/machines/shower/shower_mid1.ogg'=1,'sound/machines/shower/shower_mid2.ogg'=1,'sound/machines/shower/shower_mid3.ogg'=1)
mid_length = 10
end_sound = 'sound/machines/shower/shower_end.ogg'
- volume = 10
+ volume = 20
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,6 +12,28 @@
mid_sounds = list('sound/machines/sm/supermatter1.ogg'=1,'sound/machines/sm/supermatter2.ogg'=1,'sound/machines/sm/supermatter3.ogg'=1)
mid_length = 10
volume = 1
+ extra_range = 25
+ falloff_exponent = 10
+ falloff_distance = 5
+ vary = TRUE
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/datum/looping_sound/destabilized_crystal
+ mid_sounds = list('sound/machines/sm/loops/delamming.ogg' = 1)
+ mid_length = 60
+ volume = 55
+ extra_range = 15
+ vary = TRUE
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// /datum/looping_sound/hypertorus
+// mid_sounds = list('sound/machines/hypertorus/loops/hypertorus_nominal.ogg' = 1)
+// mid_length = 60
+// volume = 55
+// extra_range = 15
+// vary = TRUE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -32,7 +54,22 @@
mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
mid_length = 2
end_sound = 'sound/machines/fryer/deep_fryer_emerge.ogg'
- volume = 5
+ volume = 15
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+/datum/looping_sound/grill
+ mid_sounds = list('sound/machines/grill/grillsizzle.ogg' = 1)
+ mid_length = 18
+ volume = 50
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/datum/looping_sound/deep_fryer
+ mid_length = 2
+ mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
+ volume = 30
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -46,9 +83,39 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/datum/looping_sound/grill
- mid_length = 2
- mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
- volume = 10
+// /datum/looping_sound/jackpot
+// mid_length = 11
+// mid_sounds = list('sound/machines/roulettejackpot.ogg')
+// volume = 85
+// vary = TRUE
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+/datum/looping_sound/server
+ mid_sounds = list('sound/machines/tcomms/tcomms_mid1.ogg'=1,'sound/machines/tcomms/tcomms_mid2.ogg'=1,'sound/machines/tcomms/tcomms_mid3.ogg'=1,'sound/machines/tcomms/tcomms_mid4.ogg'=1,\
+ 'sound/machines/tcomms/tcomms_mid5.ogg'=1,'sound/machines/tcomms/tcomms_mid6.ogg'=1,'sound/machines/tcomms/tcomms_mid7.ogg'=1)
+ mid_length = 1.8 SECONDS
+ extra_range = -11
+ falloff_distance = 1
+ falloff_exponent = 5
+ volume = 50
+*/
+// /datum/looping_sound/computer
+// start_sound = 'sound/machines/computer/computer_start.ogg'
+// start_length = 7.2 SECONDS
+// start_volume = 10
+// mid_sounds = list('sound/machines/computer/computer_mid1.ogg'=1, 'sound/machines/computer/computer_mid2.ogg'=1)
+// mid_length = 1.8 SECONDS
+// end_sound = 'sound/machines/computer/computer_end.ogg'
+// end_volume = 10
+// volume = 2
+// falloff_exponent = 5 //Ultra quiet very fast
+// extra_range = -12
+// falloff_distance = 1 //Instant falloff after initial tile
+
+// /datum/looping_sound/gravgen
+// mid_sounds = list('sound/machines/gravgen/gravgen_mid1.ogg'=1,'sound/machines/gravgen/gravgen_mid2.ogg'=1,'sound/machines/gravgen/gravgen_mid3.ogg'=1,'sound/machines/gravgen/gravgen_mid4.ogg'=1,)
+// mid_length = 1.8 SECONDS
+// extra_range = 10
+// volume = 70
+// falloff_distance = 5
+// falloff_exponent = 20
diff --git a/code/datums/skills/_skill_modifier.dm b/code/datums/skills/_skill_modifier.dm
index c38cbf23c6..fd8de29f28 100644
--- a/code/datums/skills/_skill_modifier.dm
+++ b/code/datums/skills/_skill_modifier.dm
@@ -47,7 +47,7 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill)
if(!mod_L)
mod_L = GLOB.potential_mods_per_skill[target_skills] = list()
else
- BINARY_INSERT(identifier, mod_L, datum/skill_modifier, src, priority, COMPARE_VALUE)
+ BINARY_INSERT(identifier, mod_L, /datum/skill_modifier, src, priority, COMPARE_VALUE)
mod_L[identifier] = src
GLOB.potential_skills_per_mod[target_skills_key] = list(target_skills)
else //Should be a list.
@@ -66,7 +66,7 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill)
if(!mod_L)
mod_L = GLOB.potential_mods_per_skill[path] = list()
else
- BINARY_INSERT(identifier, mod_L, datum/skill_modifier, src, priority, COMPARE_VALUE)
+ BINARY_INSERT(identifier, mod_L, /datum/skill_modifier, src, priority, COMPARE_VALUE)
mod_L[identifier] = src
/datum/skill_modifier/Destroy()
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 19b12410bc..fe59bbe14a 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -887,7 +887,7 @@
L.remove_status_effect(STATUS_EFFECT_CHOKINGSTRAND)
-datum/status_effect/pacify
+/datum/status_effect/pacify
id = "pacify"
status_type = STATUS_EFFECT_REPLACE
tick_interval = 1
diff --git a/code/game/alternate_appearance.dm b/code/game/alternate_appearance.dm
index d7c34da34a..a1746001b9 100644
--- a/code/game/alternate_appearance.dm
+++ b/code/game/alternate_appearance.dm
@@ -168,7 +168,7 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
return TRUE
return FALSE
-datum/atom_hud/alternate_appearance/basic/onePerson
+/datum/atom_hud/alternate_appearance/basic/onePerson
var/mob/seer
/datum/atom_hud/alternate_appearance/basic/onePerson/mobShouldSee(mob/M)
diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm
index 2c2cc46344..b2673c960e 100644
--- a/code/game/area/Space_Station_13_areas.dm
+++ b/code/game/area/Space_Station_13_areas.dm
@@ -45,6 +45,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
ambientsounds = SPACE
blob_allowed = FALSE //Eating up space doesn't count for victory as a blob.
considered_hull_exterior = TRUE
+ sound_environment = SOUND_AREA_SPACE
/area/space/nearstation
icon_state = "space_near"
@@ -70,6 +71,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
blob_allowed = FALSE //Nope, no winning on the asteroid as a blob. Gotta eat the station.
valid_territory = FALSE
ambientsounds = MINING
+ sound_environment = SOUND_AREA_ASTEROID
/area/asteroid/nearstation
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
@@ -107,7 +109,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/maintenance
ambientsounds = MAINTENANCE
valid_territory = FALSE
-
+ sound_environment = SOUND_AREA_TUNNEL_ENCLOSED
//Departments
@@ -122,6 +124,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/maintenance/department/crew_quarters/bar
name = "Bar Maintenance"
icon_state = "maint_bar"
+ sound_environment = SOUND_AREA_WOODFLOOR
/area/maintenance/department/crew_quarters/dorms
name = "Dormitory Maintenance"
@@ -265,6 +268,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/hallway
nightshift_public_area = NIGHTSHIFT_AREA_PUBLIC
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/hallway/primary/aft
name = "Aft Primary Hallway"
@@ -333,30 +337,36 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Bridge"
icon_state = "bridge"
music = "signal"
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/bridge/meeting_room
name = "Heads of Staff Meeting Room"
icon_state = "meeting"
music = null
+ sound_environment = SOUND_AREA_MEDIUM_SOFTFLOOR
/area/bridge/meeting_room/council
name = "Council Chamber"
icon_state = "meeting"
music = null
+ sound_environment = SOUND_AREA_MEDIUM_SOFTFLOOR
/area/bridge/showroom/corporate
name = "Corporate Showroom"
icon_state = "showroom"
music = null
+ sound_environment = SOUND_AREA_MEDIUM_SOFTFLOOR
/area/crew_quarters/heads/captain
name = "Captain's Office"
icon_state = "captain"
clockwork_warp_allowed = FALSE
+ sound_environment = SOUND_AREA_WOODFLOOR
/area/crew_quarters/heads/captain/private
name = "Captain's Quarters"
icon_state = "captain"
+ sound_environment = SOUND_AREA_WOODFLOOR
/area/crew_quarters/heads/chief
name = "Chief Engineer's Office"
@@ -401,10 +411,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/comms
name = "Communications Relay"
icon_state = "tcomsatcham"
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/server
name = "Messaging Server Room"
icon_state = "server"
+ sound_environment = SOUND_AREA_STANDARD_STATION
//Crew
@@ -413,6 +425,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "Sleep"
safe = TRUE
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/crew_quarters/dorms/male
name = "Male Dorm"
@@ -431,6 +444,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/crew_quarters/toilet
name = "Dormitory Toilets"
icon_state = "toilet"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/crew_quarters/toilet/auxiliary
name = "Auxiliary Restrooms"
@@ -465,6 +479,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Lounge"
icon_state = "yellow"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
+ sound_environment = SOUND_AREA_SMALL_SOFTFLOOR
/area/crew_quarters/arcade
name = "Arcade"
@@ -502,15 +517,18 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/crew_quarters/kitchen/backroom
name = "Kitchen Coldroom"
icon_state = "kitchen"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/crew_quarters/bar
name = "Bar"
icon_state = "bar"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
+ sound_environment = SOUND_AREA_WOODFLOOR
/area/crew_quarters/bar/atrium
name = "Atrium"
icon_state = "bar"
+ sound_environment = SOUND_AREA_WOODFLOOR
/area/crew_quarters/electronic_marketing_den
name = "Electronic Marketing Den"
@@ -526,6 +544,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/crew_quarters/theatre
name = "Theatre"
icon_state = "Theatre"
+ sound_environment = SOUND_AREA_WOODFLOOR
/area/crew_quarters/theatre/abandoned
name = "Abandoned Theatre"
@@ -546,10 +565,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "library"
flags_1 = NONE
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
+ sound_environment = SOUND_AREA_LARGE_SOFTFLOOR
/area/library/lounge
name = "Library Lounge"
icon_state = "library"
+ sound_environment = SOUND_AREA_SMALL_SOFTFLOOR
/area/library/abandoned
name = "Abandoned Library"
@@ -564,6 +585,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
clockwork_warp_allowed = FALSE
clockwork_warp_fail = "The consecration here prevents you from warping in."
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
+ sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/chapel/main
name = "Chapel"
@@ -579,6 +601,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/chapel/asteroid
name = "Chapel Asteroid"
icon_state = "explored"
+ sound_environment = SOUND_AREA_ASTEROID
/area/chapel/asteroid/monastery
name = "Monastery Asteroid"
@@ -590,12 +613,14 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/lawoffice
name = "Law Office"
icon_state = "law"
+ sound_environment = SOUND_AREA_SMALL_SOFTFLOOR
//Engineering
/area/engine
ambientsounds = ENGINEERING
+ sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/engine/engine_smes
name = "Engineering SMES"
@@ -613,14 +638,17 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/engine/atmospherics_engine
name = "Atmospherics Engine"
icon_state = "atmos_engine"
+ sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/engine/supermatter
name = "Supermatter Engine"
icon_state = "engine_sm"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/engine/break_room
name = "Engineering Foyer"
icon_state = "engine_foyer"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/engine/gravity_generator
name = "Gravity Generator Room"
@@ -635,6 +663,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/engine/storage
name = "Engineering Storage"
icon_state = "engi_storage"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/engine/storage_shared
name = "Shared Engineering Storage"
@@ -654,10 +683,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
blob_allowed = FALSE
flags_1 = NONE
ambientsounds = ENGINEERING
+ sound_environment = SOUND_AREA_SPACE
/area/solar/fore
name = "Fore Solar Array"
icon_state = "yellow"
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/solar/aft
name = "Aft Solar Array"
@@ -763,6 +794,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "gateway"
music = "signal"
ambientsounds = ENGINEERING
+ sound_environment = SOUND_AREA_STANDARD_STATION
//MedBay
@@ -770,6 +802,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Medical"
icon_state = "medbay3"
ambientsounds = MEDICAL
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/medical/clinic
name = "Clinic"
@@ -780,6 +813,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Abandoned Medbay"
icon_state = "medbay3"
music = 'sound/ambience/signal.ogg'
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/medical/medbay/central
name = "Medbay Central"
@@ -821,6 +855,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/medical/patients_rooms
name = "Patients' Rooms"
icon_state = "patients"
+ sound_environment = SOUND_AREA_SMALL_SOFTFLOOR
/area/medical/patients_rooms/room_a
name = "Patient Room A"
@@ -839,6 +874,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Morgue"
icon_state = "morgue"
ambientsounds = SPOOKY
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/medical/chemistry
name = "Chemistry"
@@ -879,6 +915,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Security"
icon_state = "security"
ambientsounds = HIGHSEC
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/security/main
name = "Security Office"
@@ -891,6 +928,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/security/courtroom
name = "Courtroom"
icon_state = "courtroom"
+ sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/security/prison
name = "Prison Wing"
@@ -903,10 +941,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/security/processing/cremation
name = "Security Crematorium"
icon_state = "sec_prison"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/security/warden
name = "Brig Control"
icon_state = "Warden"
+ sound_environment = SOUND_AREA_SMALL_SOFTFLOOR
/area/security/armory
name = "Armory"
@@ -920,6 +960,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/security/detectives_office/private_investigators_office
name = "Private Investigator's Office"
icon_state = "detective"
+ sound_environment = SOUND_AREA_SMALL_SOFTFLOOR
/area/security/range
name = "Firing Range"
@@ -997,18 +1038,17 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/quartermaster
name = "Quartermasters"
icon_state = "quart"
-
-///////////WORK IN PROGRESS//////////
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/quartermaster/sorting
name = "Delivery Office"
icon_state = "cargo_delivery"
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/quartermaster/warehouse
name = "Warehouse"
icon_state = "cargo_warehouse"
-
-////////////WORK IN PROGRESS//////////
+ sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/quartermaster/office
name = "Cargo Office"
@@ -1017,6 +1057,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/quartermaster/storage
name = "Cargo Bay"
icon_state = "cargo_bay"
+ sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/quartermaster/qm
name = "Quartermaster's Office"
@@ -1046,6 +1087,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Custodial Closet"
icon_state = "janitor"
flags_1 = NONE
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/janitor/aux
name = "Auxiliary Custodial Closet"
@@ -1055,6 +1097,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/hydroponics
name = "Hydroponics"
icon_state = "hydro"
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/hydroponics/lobby
name = "Hydroponics Lobby"
@@ -1067,6 +1110,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/hydroponics/garden/abandoned
name = "Abandoned Garden"
icon_state = "abandoned_garden"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/hydroponics/garden/monastery
name = "Monastery Garden"
@@ -1077,6 +1121,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/science
name = "Science Division"
icon_state = "toxlab"
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/science/observatory
name = "Research Observatory"
@@ -1167,12 +1212,15 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/science/research/abandoned
name = "Abandoned Research Lab"
icon_state = "medresearch"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/science/nanite
name = "Nanite Lab"
icon_state = "toxmisc"
//Storage
+/area/storage
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/storage/tools
name = "Auxiliary Tool Storage"
@@ -1242,6 +1290,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Construction Area"
icon_state = "yellow"
ambientsounds = ENGINEERING
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/construction/minisat_exterior
name = "Minisat Exterior"
@@ -1250,6 +1299,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/construction/mining/aux_base
name = "Auxiliary Base Construction"
icon_state = "yellow"
+ sound_environment = SOUND_AREA_MEDIUM_SOFTFLOOR
/area/construction/mining/aux_base/closet
name = "Auxiliary Closet Construction"
@@ -1305,6 +1355,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
//AI
+/area/ai_monitored
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/ai_monitored/security/armory
name = "Armory"
@@ -1329,10 +1381,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/ai_monitored/turret_protected/ai_upload
name = "AI Upload Chamber"
icon_state = "ai_upload"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/ai_monitored/turret_protected/ai_upload_foyer
name = "AI Upload Access"
icon_state = "ai_foyer"
+ sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/ai_monitored/turret_protected/ai
name = "AI Chamber"
@@ -1341,6 +1395,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/ai_monitored/turret_protected/aisat
name = "AI Satellite"
icon_state = "ai"
+ sound_environment = SOUND_ENVIRONMENT_ROOM
/area/ai_monitored/turret_protected/aisat/atmos
name = "AI Satellite Atmos"
@@ -1365,6 +1420,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/ai_monitored/turret_protected/aisat_interior
name = "AI Satellite Antechamber"
icon_state = "ai"
+ sound_environment = SOUND_AREA_LARGE_ENCLOSED
/area/ai_monitored/turret_protected/AIsatextFP
name = "AI Sat Ext"
@@ -1418,6 +1474,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/tcommsat/computer
name = "Telecomms Control Room"
icon_state = "tcomsatcomp"
+ sound_environment = SOUND_AREA_MEDIUM_SOFTFLOOR
/area/tcommsat/server
name = "Telecomms Server Room"
diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm
index 558a4b1026..48e77e1623 100644
--- a/code/game/area/ai_monitored.dm
+++ b/code/game/area/ai_monitored.dm
@@ -3,6 +3,7 @@
clockwork_warp_allowed = FALSE
var/list/obj/machinery/camera/motioncameras = list()
var/list/datum/weakref/motionTargets = list()
+ sound_environment = SOUND_ENVIRONMENT_ROOM
/area/ai_monitored/Initialize(mapload)
. = ..()
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index ff177898cb..4790d20f64 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -1,16 +1,72 @@
-// Areas.dm
-
-
+/**
+ * # area
+ *
+ * A grouping of tiles into a logical space, mostly used by map editors
+ */
/area
- level = null
name = "Space"
icon = 'icons/turf/areas.dmi'
icon_state = "unknown"
layer = AREA_LAYER
- plane = BLACKNESS_PLANE //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE.
+ //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE.
+ plane = BLACKNESS_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
invisibility = INVISIBILITY_LIGHTING
+ var/fire = null
+ ///Whether there is an atmos alarm in this area
+ var/atmosalm = FALSE
+ var/poweralm = FALSE
+ var/lightswitch = TRUE
+
+ /// All beauty in this area combined, only includes indoor area.
+ var/totalbeauty = 0
+ /// Beauty average per open turf in the area
+ var/beauty = 0
+ /// If a room is too big it doesn't have beauty.
+ var/beauty_threshold = 150
+
+ /// For space, the asteroid, lavaland, etc. Used with blueprints or with weather to determine if we are adding a new area (vs editing a station room)
+ var/outdoors = FALSE
+
+ /// Size of the area in open turfs, only calculated for indoors areas.
+ var/areasize = 0
+
+ /// Bonus mood for being in this area
+ var/mood_bonus = 0
+ /// Mood message for being here, only shows up if mood_bonus != 0
+ var/mood_message = "This area is pretty nice!\n"
+
+ ///Will objects this area be needing power?
+ var/requires_power = TRUE
+ /// This gets overridden to 1 for space in area/Initialize().
+ var/always_unpowered = FALSE
+
+ var/power_equip = TRUE
+ var/power_light = TRUE
+ var/power_environ = TRUE
+
+ var/has_gravity = FALSE
+
+ var/parallax_movedir = 0
+
+ var/list/ambientsounds = GENERIC
+ flags_1 = CAN_BE_DIRTY_1
+
+ var/list/firedoors
+ var/list/cameras
+ var/list/firealarms
+ var/firedoors_last_closed_on = 0
+
+
+ ///This datum, if set, allows terrain generation behavior to be ran on Initialize()
+ // var/datum/map_generator/map_generator
+
+ ///Used to decide what kind of reverb the area makes sound have
+ var/sound_environment = SOUND_ENVIRONMENT_NONE
+
+ /// CIT SPECIFIC VARS
+
/// Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints.
var/map_name
@@ -41,29 +97,8 @@
/// Considered space for hull shielding
var/considered_hull_exterior = FALSE
- var/fire = null
var/atmos = TRUE
- var/atmosalm = FALSE
- var/poweralm = TRUE
- var/lightswitch = TRUE
- var/totalbeauty = 0 //All beauty in this area combined, only includes indoor area.
- var/beauty = 0 // Beauty average per open turf in the area
- var/beauty_threshold = 150 //If a room is too big it doesn't have beauty.
-
- var/requires_power = TRUE
- /// This gets overridden to 1 for space in area/Initialize().
- var/always_unpowered = FALSE
-
- /// For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room)
- var/outdoors = FALSE
-
- /// Size of the area in open turfs, only calculated for indoors areas.
- var/areasize = 0
-
- var/power_equip = TRUE
- var/power_light = TRUE
- var/power_environ = TRUE
var/music = null
var/used_equip = 0
var/used_light = 0
@@ -72,7 +107,6 @@
var/static_light = 0
var/static_environ
- var/has_gravity = 0
/// Are you forbidden from teleporting to the area? (centcom, mobs, wizard, hand teleporter)
var/noteleport = FALSE
/// Hides area from player Teleport function.
@@ -84,15 +118,6 @@
var/no_air = null
- var/parallax_movedir = 0
-
- var/list/ambientsounds = GENERIC
- flags_1 = CAN_BE_DIRTY_1
-
- var/list/firedoors
- var/list/cameras
- var/list/firealarms
- var/firedoors_last_closed_on = 0
var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
var/list/canSmoothWithAreas //typecache to limit the areas that atoms in this area can smooth with
@@ -111,10 +136,24 @@
var/nightshift_public_area = NIGHTSHIFT_AREA_NONE //considered a public area for nightshift
-/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
-/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/
+
+/**
+ * A list of teleport locations
+ *
+ * Adding a wizard area teleport list because motherfucking lag -- Urist
+ * I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game
+ */
GLOBAL_LIST_EMPTY(teleportlocs)
+/**
+ * Generate a list of turfs you can teleport to from the areas list
+ *
+ * Includes areas if they're not a shuttle or not not teleport or have no contents
+ *
+ * The chosen turf is the first item in the areas contents that is a station level
+ *
+ * The returned list of turfs is sorted by name
+ */
/proc/process_teleport_locs()
for(var/V in GLOB.sortedAreas)
var/area/AR = V
@@ -128,11 +167,19 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if (picked && is_station_level(picked.z))
GLOB.teleportlocs[AR.name] = AR
- sortTim(GLOB.teleportlocs, /proc/cmp_text_dsc)
-
-// ===
+ sortTim(GLOB.teleportlocs, /proc/cmp_text_asc)
+/**
+ * Called when an area loads
+ *
+ * Adds the item to the GLOB.areas_by_type list based on area type
+ */
/area/New()
+ // This interacts with the map loader, so it needs to be set immediately
+ // rather than waiting for atoms to initialize.
+ if (unique)
+ GLOB.areas_by_type[type] = src
+
if(!minimap_color) // goes in New() because otherwise it doesn't fucking work
// generate one using the icon_state
if(icon_state && icon_state != "unknown")
@@ -141,14 +188,18 @@ GLOBAL_LIST_EMPTY(teleportlocs)
minimap_color = I.GetPixel(1,1)
else // no icon state? use random.
minimap_color = rgb(rand(50,70),rand(50,70),rand(50,70)) // This interacts with the map loader, so it needs to be set immediately
- // rather than waiting for atoms to initialize.
- if (unique)
- GLOB.areas_by_type[type] = src
return ..()
+/**
+ * Initalize this area
+ *
+ * intializes the dynamic area lighting and also registers the area with the z level via
+ * reg_in_areas_in_z
+ *
+ * returns INITIALIZE_HINT_LATELOAD
+ */
/area/Initialize()
icon_state = ""
- layer = AREA_LAYER
map_name = name // Save the initial (the name set in the map) name of the area.
canSmoothWithAreas = typecacheof(canSmoothWithAreas)
@@ -202,29 +253,55 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return INITIALIZE_HINT_LATELOAD
+/**
+ * Sets machine power levels in the area
+ */
/area/LateInitialize()
if(!base_area) //we don't want to run it twice.
power_change() // all machines set to current power level, also updates icon
update_beauty()
-/area/proc/reg_in_areas_in_z()
- if(contents.len)
- var/list/areas_in_z = SSmapping.areas_in_z
- var/z
- update_areasize()
- for(var/i in 1 to contents.len)
- var/atom/thing = contents[i]
- if(!thing)
- continue
- z = thing.z
- break
- if(!z)
- WARNING("No z found for [src]")
- return
- if(!areas_in_z["[z]"])
- areas_in_z["[z]"] = list()
- areas_in_z["[z]"] += src
+/// Soon ™
+/area/proc/RunGeneration()
+ // if(map_generator)
+ // map_generator = new map_generator()
+ // var/list/turfs = list()
+ // for(var/turf/T in contents)
+ // turfs += T
+ // map_generator.generate_terrain(turfs)
+/area/proc/test_gen()
+ // if(map_generator)
+ // var/list/turfs = list()
+ // for(var/turf/T in contents)
+ // turfs += T
+ // map_generator.generate_terrain(turfs)
+
+/**
+ * Register this area as belonging to a z level
+ *
+ * Ensures the item is added to the SSmapping.areas_in_z list for this z
+ */
+/area/proc/reg_in_areas_in_z()
+ if(!length(contents))
+ return
+ var/list/areas_in_z = SSmapping.areas_in_z
+ update_areasize()
+ if(!z)
+ WARNING("No z found for [src]")
+ return
+ if(!areas_in_z["[z]"])
+ areas_in_z["[z]"] = list()
+ areas_in_z["[z]"] += src
+
+/**
+ * Destroy an area and clean it up
+ *
+ * Removes the area from GLOB.areas_by_type and also stops it processing on SSobj
+ *
+ * This is despite the fact that no code appears to put it on SSobj, but
+ * who am I to argue with old coders
+ */
/area/Destroy()
if(GLOB.areas_by_type[type] == src)
GLOB.areas_by_type[type] = null
@@ -244,6 +321,11 @@ GLOBAL_LIST_EMPTY(teleportlocs)
STOP_PROCESSING(SSobj, src)
return ..()
+/**
+ * Generate a power alert for this area
+ *
+ * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world
+ */
/area/proc/poweralert(state, obj/source)
if (state != poweralm)
poweralm = state
@@ -525,6 +607,13 @@ GLOBAL_LIST_EMPTY(teleportlocs)
used_environ += amount
+/**
+ * Call back when an atom enters an area
+ *
+ * Sends signals COMSIG_AREA_ENTERED and COMSIG_ENTER_AREA (to the atom)
+ *
+ * If the area has ambience, then it plays some ambience music to the ambience channel
+ */
/area/Entered(atom/movable/M, atom/OldLoc)
set waitfor = FALSE
SEND_SIGNAL(src, COMSIG_AREA_ENTERED, M)
@@ -567,6 +656,12 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return FALSE //Too big
beauty = totalbeauty / areasize
+
+/**
+ * Called when an atom exits an area
+ *
+ * Sends signals COMSIG_AREA_EXITED and COMSIG_EXIT_AREA (to the atom)
+ */
/area/Exited(atom/movable/M)
SEND_SIGNAL(src, COMSIG_AREA_EXITED, M)
SEND_SIGNAL(M, COMSIG_EXIT_AREA, src) //The atom that exits the area
diff --git a/code/game/area/areas/away_content.dm b/code/game/area/areas/away_content.dm
index 27a73ae5f1..63beaec412 100644
--- a/code/game/area/areas/away_content.dm
+++ b/code/game/area/areas/away_content.dm
@@ -9,6 +9,7 @@ Unused icons for new areas are "awaycontent1" ~ "awaycontent30"
icon_state = "away"
has_gravity = STANDARD_GRAVITY
ambientsounds = AWAY_MISSION
+ sound_environment = SOUND_ENVIRONMENT_ROOM
/area/awaymission/beach
name = "Beach"
diff --git a/code/game/area/areas/holodeck.dm b/code/game/area/areas/holodeck.dm
index 9eec00460a..c0819d29b9 100644
--- a/code/game/area/areas/holodeck.dm
+++ b/code/game/area/areas/holodeck.dm
@@ -4,6 +4,7 @@
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
flags_1 = 0
hidden = TRUE
+ sound_environment = SOUND_ENVIRONMENT_PADDED_CELL
var/obj/machinery/computer/holodeck/linked
var/restricted = 0 // if true, program goes on emag list
diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm
index 70e0910dde..520c7ba59f 100644
--- a/code/game/area/areas/mining.dm
+++ b/code/game/area/areas/mining.dm
@@ -19,6 +19,7 @@
flags_1 = NONE
ambientsounds = MINING
flora_allowed = FALSE
+ sound_environment = SOUND_AREA_STANDARD_STATION
/area/mine/unexplored
name = "Mine"
@@ -86,6 +87,7 @@
has_gravity = STANDARD_GRAVITY
flags_1 = NONE
flora_allowed = TRUE
+ sound_environment = SOUND_AREA_LAVALAND
/area/lavaland/surface
name = "Lavaland"
@@ -139,6 +141,7 @@
flags_1 = NONE
flora_allowed = TRUE
blob_allowed = FALSE
+ sound_environment = SOUND_AREA_ICEMOON
/area/icemoon/surface
name = "Icemoon"
diff --git a/code/game/area/areas/ruins/_ruins.dm b/code/game/area/areas/ruins/_ruins.dm
index b97c3f0ef4..17ba4f7721 100644
--- a/code/game/area/areas/ruins/_ruins.dm
+++ b/code/game/area/areas/ruins/_ruins.dm
@@ -7,6 +7,7 @@
hidden = TRUE
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
ambientsounds = RUINS
+ sound_environment = SOUND_ENVIRONMENT_STONEROOM
/area/ruin/unpowered
diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm
index e7f8c63d4a..9a890ead75 100644
--- a/code/game/area/areas/shuttles.dm
+++ b/code/game/area/areas/shuttles.dm
@@ -12,6 +12,7 @@
icon_state = "shuttle"
// Loading the same shuttle map at a different time will produce distinct area instances.
unique = FALSE
+ sound_environment = SOUND_ENVIRONMENT_ROOM
/area/shuttle/Initialize()
if(!canSmoothWithAreas)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 7fbfe48f4e..189640a1c6 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1,12 +1,22 @@
+/**
+ * The base type for nearly all physical objects in SS13
+
+ * Lots and lots of functionality lives here, although in general we are striving to move
+ * as much as possible to the components/elements system
+ */
/atom
layer = TURF_LAYER
plane = GAME_PLANE
- var/level = 2
- var/article // If non-null, overrides a/an/some in all cases
+ appearance_flags = TILE_BOUND
+ var/level = 2
+ ///If non-null, overrides a/an/some in all cases
+ var/article
+
+ ///First atom flags var
var/flags_1 = NONE
+ ///Intearaction flags
var/interaction_flags_atom = NONE
- var/datum/reagents/reagents = null
var/flags_ricochet = NONE
@@ -15,35 +25,52 @@
///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom
var/ricochet_damage_mod = 0.33
- //This atom's HUD (med/sec, etc) images. Associative list.
+ ///Reagents holder
+ var/datum/reagents/reagents = null
+
+ ///This atom's HUD (med/sec, etc) images. Associative list.
var/list/image/hud_list = null
- //HUD images that this atom can provide.
+ ///HUD images that this atom can provide.
var/list/hud_possible
- //Value used to increment ex_act() if reactionary_explosions is on
+ ///Value used to increment ex_act() if reactionary_explosions is on
var/explosion_block = 0
- var/list/atom_colours //used to store the different colors on an atom
- //its inherent color, the colored paint applied on it, special color effect etc...
+ /**
+ * used to store the different colors on an atom
+ *
+ * its inherent color, the colored paint applied on it, special color effect etc...
+ */
+ var/list/atom_colours
- var/list/remove_overlays // a very temporary list of overlays to remove
- var/list/add_overlays // a very temporary list of overlays to add
- var/list/managed_vis_overlays //vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays
- ///overlays managed by update_overlays() to prevent removing overlays that weren't added by the same proc
+ /// a very temporary list of overlays to remove
+ var/list/remove_overlays
+ /// a very temporary list of overlays to add
+ var/list/add_overlays
+
+ ///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays
+ var/list/managed_vis_overlays
+ ///overlays managed by [update_overlays][/atom/proc/update_overlays] to prevent removing overlays that weren't added by the same proc
var/list/managed_overlays
+ ///Proximity monitor associated with this atom
var/datum/proximity_monitor/proximity_monitor
+ ///Last fingerprints to touch this atom
var/fingerprintslast
var/list/filter_data //For handling persistent filters
+ ///Price of an item in a vending machine, overriding the base vending machine price. Define in terms of paycheck defines as opposed to raw numbers.
var/custom_price
+ ///Price of an item in a vending machine, overriding the premium vending machine price. Define in terms of paycheck defines as opposed to raw numbers.
var/custom_premium_price
+ //List of datums orbiting this atom
var/datum/component/orbiter/orbiters
var/rad_flags = NONE // Will move to flags_1 when i can be arsed to
+ /// Radiation insulation types
var/rad_insulation = RAD_NO_INSULATION
///The custom materials this atom is made of, used by a lot of things like furniture, walls, and floors (if I finish the functionality, that is.)
@@ -72,6 +99,16 @@
///Mobs that are currently do_after'ing this atom, to be cleared from on Destroy()
var/list/targeted_by
+/**
+ * Called when an atom is created in byond (built in engine proc)
+ *
+ * Not a lot happens here in SS13 code, as we offload most of the work to the
+ * [Intialization][/atom/proc/Initialize] proc, mostly we run the preloader
+ * if the preloader is being used and then call [InitAtom][/datum/controller/subsystem/atoms/proc/InitAtom] of which the ultimate
+ * result is that the Intialize proc is called.
+ *
+ * We also generate a tag here if the DF_USE_TAG flag is set on the atom
+ */
/atom/New(loc, ...)
//atom creation method that preloads variables at creation
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
@@ -87,24 +124,50 @@
//we were deleted
return
-//Called after New if the map is being loaded. mapload = TRUE
-//Called from base of New if the map is not being loaded. mapload = FALSE
-//This base must be called or derivatives must set initialized to TRUE
-//must not sleep
-//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE
-//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm
-
-//Note: the following functions don't call the base for optimization and must copypasta:
-// /turf/Initialize
-// /turf/open/space/Initialize
-
+/**
+ * The primary method that objects are setup in SS13 with
+ *
+ * we don't use New as we have better control over when this is called and we can choose
+ * to delay calls or hook other logic in and so forth
+ *
+ * During roundstart map parsing, atoms are queued for intialization in the base atom/New(),
+ * After the map has loaded, then Initalize is called on all atoms one by one. NB: this
+ * is also true for loading map templates as well, so they don't Initalize until all objects
+ * in the map file are parsed and present in the world
+ *
+ * If you're creating an object at any point after SSInit has run then this proc will be
+ * immediately be called from New.
+ *
+ * mapload: This parameter is true if the atom being loaded is either being intialized during
+ * the Atom subsystem intialization, or if the atom is being loaded from the map template.
+ * If the item is being created at runtime any time after the Atom subsystem is intialized then
+ * it's false.
+ *
+ * You must always call the parent of this proc, otherwise failures will occur as the item
+ * will not be seen as initalized (this can lead to all sorts of strange behaviour, like
+ * the item being completely unclickable)
+ *
+ * You must not sleep in this proc, or any subprocs
+ *
+ * Any parameters from new are passed through (excluding loc), naturally if you're loading from a map
+ * there are no other arguments
+ *
+ * Must return an [initialization hint][INITIALIZE_HINT_NORMAL] or a runtime will occur.
+ *
+ * Note: the following functions don't call the base for optimization and must copypasta handling:
+ * * [/turf/proc/Initialize]
+ * * [/turf/open/space/proc/Initialize]
+ */
/atom/proc/Initialize(mapload, ...)
+ // SHOULD_NOT_SLEEP(TRUE)
+ SHOULD_CALL_PARENT(TRUE)
if(flags_1 & INITIALIZED_1)
stack_trace("Warning: [src]([type]) initialized multiple times!")
flags_1 |= INITIALIZED_1
if(loc)
SEND_SIGNAL(loc, COMSIG_ATOM_CREATED, src) /// Sends a signal that the new atom `src`, has been created at `loc`
+
//atom color stuff
if(color)
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
@@ -126,14 +189,34 @@
return INITIALIZE_HINT_NORMAL
-//called if Initialize returns INITIALIZE_HINT_LATELOAD
+/**
+ * Late Intialization, for code that should run after all atoms have run Intialization
+ *
+ * To have your LateIntialize proc be called, your atoms [Initalization][/atom/proc/Initialize]
+ * proc must return the hint
+ * [INITIALIZE_HINT_LATELOAD] otherwise you will never be called.
+ *
+ * useful for doing things like finding other machines on GLOB.machines because you can guarantee
+ * that all atoms will actually exist in the "WORLD" at this time and that all their Intialization
+ * code has been run
+ */
/atom/proc/LateInitialize()
- return
+ set waitfor = FALSE
-// Put your AddComponent() calls here
+/// Put your [AddComponent] calls here
/atom/proc/ComponentInitialize()
return
+/**
+ * Top level of the destroy chain for most atoms
+ *
+ * Cleans up the following:
+ * * Removes alternate apperances from huds that see them
+ * * qdels the reagent holder from atoms if it exists
+ * * clears the orbiters list
+ * * clears overlays and priority overlays
+ * * clears the light object
+ */
/atom/Destroy()
if(alternate_appearances)
for(var/K in alternate_appearances)
@@ -143,6 +226,8 @@
if(reagents)
qdel(reagents)
+ orbiters = null // The component is attached to us normaly and will be deleted elsewhere
+
LAZYCLEARLIST(overlays)
for(var/i in targeted_by)
@@ -179,6 +264,16 @@
/atom/proc/CanPass(atom/movable/mover, turf/target)
return !density
+/**
+ * Is this atom currently located on centcom
+ *
+ * Specifically, is it on the z level and within the centcom areas
+ *
+ * You can also be in a shuttleshuttle during endgame transit
+ *
+ * Used in gamemode to identify mobs who have escaped and for some other areas of the code
+ * who don't want atoms where they shouldn't be
+ */
/atom/proc/onCentCom()
var/turf/T = get_turf(src)
if(!T)
@@ -209,6 +304,13 @@
if(T in shuttle_area)
return TRUE
+/**
+ * Is the atom in any of the centcom syndicate areas
+ *
+ * Either in the syndie base on centcom, or any of their shuttles
+ *
+ * Also used in gamemode code for win conditions
+ */
/atom/proc/onSyndieBase()
var/turf/T = get_turf(src)
if(!T)
@@ -222,6 +324,23 @@
return FALSE
+/**
+ * Is the atom in an away mission
+ *
+ * Must be in the away mission z-level to return TRUE
+ *
+ * Also used in gamemode code for win conditions
+ */
+/atom/proc/onAwayMission()
+ var/turf/T = get_turf(src)
+ if(!T)
+ return FALSE
+
+ if(is_away_level(T.z))
+ return TRUE
+
+ return FALSE
+
/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user)
if(does_attack_animation)
@@ -415,7 +534,7 @@
/// Updates the overlays of the atom
/atom/proc/update_overlays()
- SHOULD_CALL_PARENT(1)
+ SHOULD_CALL_PARENT(TRUE)
. = list()
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .)
@@ -429,7 +548,7 @@
/atom/proc/contents_explosion(severity, target)
return //For handling the effects of explosions on contents that would not normally be effected
-/atom/proc/ex_act(severity, target)
+/atom/proc/ex_act(severity, target, datum/explosion/E)
set waitfor = FALSE
contents_explosion(severity, target)
SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 4715c3bed3..a582fa03e4 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -1,5 +1,7 @@
/atom/movable
layer = OBJ_LAYER
+ glide_size = 8
+ SET_APPEARANCE_FLAGS(TILE_BOUND | PIXEL_SCALE)
var/last_move = null
var/last_move_time = 0
var/anchored = FALSE
@@ -28,10 +30,15 @@
var/atom/movable/moving_from_pull //attempt to resume grab after moving instead of before.
var/list/client_mobs_in_contents // This contains all the client mobs within this container
var/list/acted_explosions //for explosion dodging
- glide_size = 8
- appearance_flags = TILE_BOUND|PIXEL_SCALE
var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
- var/movement_type = GROUND //Incase you have multiple types, you automatically use the most useful one. IE: Skating on ice, flippers on water, flying over chasm/space, etc.
+
+ /**
+ * In case you have multiple types, you automatically use the most useful one.
+ * IE: Skating on ice, flippers on water, flying over chasm/space, etc.
+ * I reccomend you use the movetype_handler system and not modify this directly, especially for living mobs.
+ */
+ var/movement_type = GROUND
+
var/atom/movable/pulling
var/grab_state = 0
var/throwforce = 0
@@ -59,6 +66,42 @@
em_block = new(src, render_target)
vis_contents += em_block
+
+/atom/movable/Destroy(force)
+ QDEL_NULL(proximity_monitor)
+ QDEL_NULL(language_holder)
+ QDEL_NULL(em_block)
+
+ unbuckle_all_mobs(force = TRUE)
+
+ if(loc)
+ //Restore air flow if we were blocking it (movables with ATMOS_PASS_PROC will need to do this manually if necessary)
+ if(((CanAtmosPass == ATMOS_PASS_DENSITY && density) || CanAtmosPass == ATMOS_PASS_NO) && isturf(loc))
+ CanAtmosPass = ATMOS_PASS_YES
+ air_update_turf(TRUE)
+ loc.handle_atom_del(src)
+
+ // if(opacity)
+ // RemoveElement(/datum/element/light_blocking)
+
+ invisibility = INVISIBILITY_ABSTRACT
+
+ if(pulledby)
+ pulledby.stop_pulling()
+
+ if(orbiting)
+ orbiting.end_orbit(src)
+ orbiting = null
+
+ . = ..()
+
+ for(var/movable_content in contents)
+ qdel(movable_content)
+
+ LAZYCLEARLIST(client_mobs_in_contents)
+
+ moveToNullspace()
+
/atom/movable/proc/update_emissive_block()
if(blocks_emissive != EMISSIVE_BLOCK_GENERIC)
return
@@ -113,40 +156,52 @@
return FALSE
return T.zPassOut(src, direction, destination) && destination.zPassIn(src, direction, T)
-/atom/movable/vv_edit_var(var_name, var_value)
- var/static/list/banned_edits = list("step_x", "step_y", "step_size")
- var/static/list/careful_edits = list("bound_x", "bound_y", "bound_width", "bound_height")
- if(var_name in banned_edits)
+/atom/movable/vv_edit_var(var_name, var_value, massedit)
+ var/static/list/banned_edits = list("step_x" = TRUE, "step_y" = TRUE, "step_size" = TRUE, "bounds" = TRUE)
+ var/static/list/careful_edits = list("bound_x" = TRUE, "bound_y" = TRUE, "bound_width" = TRUE, "bound_height" = TRUE)
+ if(banned_edits[var_name])
return FALSE //PLEASE no.
- if((var_name in careful_edits) && (var_value % world.icon_size) != 0)
+ if((careful_edits[var_name]) && (var_value % world.icon_size) != 0)
return FALSE
+
switch(var_name)
if(NAMEOF(src, x))
var/turf/T = locate(var_value, y, z)
if(T)
- forceMove(T)
+ admin_teleport(T, !massedit)
return TRUE
return FALSE
if(NAMEOF(src, y))
var/turf/T = locate(x, var_value, z)
if(T)
- forceMove(T)
+ admin_teleport(T, !massedit)
return TRUE
return FALSE
if(NAMEOF(src, z))
var/turf/T = locate(x, y, var_value)
if(T)
- forceMove(T)
+ admin_teleport(T, !massedit)
return TRUE
return FALSE
if(NAMEOF(src, loc))
- if(istype(var_value, /atom))
- forceMove(var_value)
- return TRUE
- else if(isnull(var_value))
- moveToNullspace()
+ if(isatom(var_value) || isnull(var_value))
+ admin_teleport(var_value, !massedit)
return TRUE
return FALSE
+ if(NAMEOF(src, anchored))
+ set_anchored(var_value)
+ . = TRUE
+ if(NAMEOF(src, pulledby))
+ set_pulledby(var_value)
+ . = TRUE
+ if(NAMEOF(src, glide_size))
+ set_glide_size(var_value)
+ . = TRUE
+
+ if(!isnull(.))
+ datum_flags |= DF_VAR_EDITED
+ return
+
return ..()
/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
@@ -168,48 +223,73 @@
AMob.grabbedby(src)
return TRUE
stop_pulling()
+
+ // SEND_SIGNAL(src, COMSIG_ATOM_START_PULL, AM, state, force)
+
if(AM.pulledby)
log_combat(AM, AM.pulledby, "pulled from", src)
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
- AM.pulledby = src
+ AM.set_pulledby(src)
setGrabState(state)
if(ismob(AM))
var/mob/M = AM
log_combat(src, M, "grabbed", addition="passive grab")
if(!supress_message)
- visible_message("[src] has grabbed [M] passively!")
+ M.visible_message("[src] grabs [M] passively.", \
+ "[src] grabs you passively.")
return TRUE
/atom/movable/proc/stop_pulling()
if(!pulling)
return
- pulling.pulledby = null
+ pulling.set_pulledby(null)
var/mob/living/ex_pulled = pulling
+ setGrabState(GRAB_PASSIVE)
pulling = null
- setGrabState(0)
if(isliving(ex_pulled))
var/mob/living/L = ex_pulled
L.update_mobility()// mob gets up if it was lyng down in a chokehold
+///Reports the event of the change in value of the pulledby variable.
+/atom/movable/proc/set_pulledby(new_pulledby)
+ if(new_pulledby == pulledby)
+ return FALSE //null signals there was a change, be sure to return FALSE if none happened here.
+ . = pulledby
+ pulledby = new_pulledby
+
/atom/movable/proc/Move_Pulled(atom/A)
if(!pulling)
- return
+ return FALSE
if(pulling.anchored || pulling.move_resist > move_force || !pulling.Adjacent(src))
stop_pulling()
- return
+ return FALSE
if(isliving(pulling))
var/mob/living/L = pulling
if(L.buckled && L.buckled.buckle_prevents_pull) //if they're buckled to something that disallows pulling, prevent it
stop_pulling()
- return
+ return FALSE
if(A == loc && pulling.density)
- return
- if(!Process_Spacemove(get_dir(pulling.loc, A)))
- return
- step(pulling, get_dir(pulling.loc, A))
+ return FALSE
+ var/move_dir = get_dir(pulling.loc, A)
+ if(!Process_Spacemove(move_dir))
+ return FALSE
+ pulling.Move(get_step(pulling.loc, move_dir), move_dir, glide_size)
return TRUE
+/**
+ * Recursively set glide size for atom's pulled things
+ */
+/atom/movable/proc/recursive_pulled_glidesize_update()
+ var/list/ran = list()
+ var/atom/movable/updating = pulling
+ while(updating)
+ if(ran[updating])
+ return
+ updating.set_glide_size(glide_size, FALSE)
+ ran[updating] = TRUE
+ updating = updating.pulling
+
/atom/movable/proc/check_pulling()
if(pulling)
var/atom/movable/pullee = pulling
@@ -229,54 +309,57 @@
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
-/atom/movable/Destroy(force)
- QDEL_NULL(proximity_monitor)
- QDEL_NULL(language_holder)
- QDEL_NULL(em_block)
+/atom/movable/proc/set_glide_size(target = 8, recursive = TRUE)
+#ifdef SMOOTH_MOVEMENT
+ // SEND_SIGNAL(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, target)
+ glide_size = target
- unbuckle_all_mobs(force=1)
+ for(var/m in buckled_mobs)
+ var/mob/buckled_mob = m
+ buckled_mob.set_glide_size(target)
- . = ..()
+ if(recursive)
+ recursive_pulled_glidesize_update()
+#else
+ return
+#endif
- if(loc)
- //Restore air flow if we were blocking it (movables with ATMOS_PASS_PROC will need to do this manually if necessary)
- if(((CanAtmosPass == ATMOS_PASS_DENSITY && density) || CanAtmosPass == ATMOS_PASS_NO) && isturf(loc))
- CanAtmosPass = ATMOS_PASS_YES
- air_update_turf(TRUE)
- loc.handle_atom_del(src)
- for(var/atom/movable/AM in contents)
- qdel(AM)
- moveToNullspace()
- invisibility = INVISIBILITY_ABSTRACT
- if(pulledby)
- pulledby.stop_pulling()
-
- if(orbiting)
- orbiting.end_orbit(src)
- orbiting = null
+///Sets the anchored var and returns if it was sucessfully changed or not.
+/atom/movable/proc/set_anchored(anchorvalue)
+ SHOULD_CALL_PARENT(TRUE)
+ if(anchored == anchorvalue)
+ return
+ . = anchored
+ anchored = anchorvalue
+ // SEND_SIGNAL(src, COMSIG_MOVABLE_SET_ANCHORED, anchorvalue)
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- set waitfor = 0
+ set waitfor = FALSE
var/hitpush = TRUE
var/impact_signal = SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
if(impact_signal & COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH)
hitpush = FALSE // hacky, tie this to something else or a proper workaround later
- if(impact_signal & ~COMPONENT_MOVABLE_IMPACT_NEVERMIND) // in case a signal interceptor broke or deleted the thing before we could process our hit
- return hit_atom.hitby(src, throwingdatum = throwingdatum, hitpush = hitpush)
+ if(!(impact_signal && (impact_signal & COMPONENT_MOVABLE_IMPACT_NEVERMIND))) // in case a signal interceptor broke or deleted the thing before we could process our hit
+ return hit_atom.hitby(src, throwingdatum=throwingdatum, hitpush=hitpush)
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum)
if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO))))
step(src, AM.dir)
..()
-/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, messy_throw = TRUE)
+/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE)
if((force < (move_resist * MOVE_FORCE_THROW_RATIO)) || (move_resist == INFINITY))
return
- return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force, messy_throw)
+ return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force, gentle)
-/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, messy_throw = TRUE) //If this returns FALSE then callback will not be called.
+///If this returns FALSE then callback will not be called.
+/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE, quickstart = TRUE)
. = FALSE
+
+ if(QDELETED(src))
+ CRASH("Qdeleted thing being thrown around.")
+
if (!target || speed <= 0)
return
@@ -288,7 +371,7 @@
//They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw?
if (thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag*2)
- var/user_momentum = thrower.movement_delay()
+ var/user_momentum = thrower.movement_delay() //cached_multiplicative_slowdown
if (!user_momentum) //no movement_delay, this means they move once per byond tick, lets calculate from that instead.
user_momentum = world.tick_lag
@@ -312,19 +395,13 @@
. = TRUE // No failure conditions past this point.
- var/datum/thrownthing/TT = new()
- TT.thrownthing = src
- TT.target = target
- TT.target_turf = get_turf(target)
- TT.init_dir = get_dir(src, target)
- TT.maxrange = range
- TT.speed = speed
- TT.thrower = thrower
- TT.diagonals_first = diagonals_first
- TT.force = force
- TT.callback = callback
- if(!QDELETED(thrower))
- TT.target_zone = thrower.zone_selected
+ var/target_zone
+ if(QDELETED(thrower))
+ thrower = null //Let's not pass a qdeleting reference if any.
+ else
+ target_zone = thrower.zone_selected
+
+ var/datum/thrownthing/TT = new(src, target, get_turf(target), get_dir(src, target), range, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
@@ -359,7 +436,8 @@
SSthrowing.processing[src] = TT
if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun))
SSthrowing.currentrun[src] = TT
- TT.tick()
+ if (quickstart)
+ TT.tick()
/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
return FALSE
@@ -382,13 +460,13 @@
return TRUE
return ..()
-// called when this atom is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
-/atom/movable/proc/on_exit_storage(datum/component/storage/concrete/S)
- return
+/// called when this atom is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
+/atom/movable/proc/on_exit_storage(datum/component/storage/concrete/S) // rename S to master_storage
+ // SEND_SIGNAL(src, COMSIG_STORAGE_EXITED, master_storage)
-// called when this atom is added into a storage item, which is passed on as S. The loc variable is already set to the storage item.
+/// called when this atom is added into a storage item, which is passed on as S. The loc variable is already set to the storage item.
/atom/movable/proc/on_enter_storage(datum/component/storage/concrete/S)
- return
+ // SEND_SIGNAL(src, COMSIG_STORAGE_ENTERED, master_storage)
/atom/movable/proc/get_spacemove_backup()
var/atom/movable/dense_object_backup
@@ -422,24 +500,26 @@
return //don't do an animation if attacking self
var/pixel_x_diff = 0
var/pixel_y_diff = 0
+ var/turn_dir = 1
var/direction = get_dir(src, A)
if(direction & NORTH)
pixel_y_diff = 8
+ turn_dir = prob(50) ? -1 : 1
else if(direction & SOUTH)
pixel_y_diff = -8
+ turn_dir = prob(50) ? -1 : 1
if(direction & EAST)
pixel_x_diff = 8
else if(direction & WEST)
pixel_x_diff = -8
+ turn_dir = -1
- var/matrix/OM = matrix(transform)
- var/matrix/M = matrix(transform)
- M.Turn(pixel_x_diff ? pixel_x_diff*2 : pick(-16, 16))
-
- animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, transform = M, time = 2)
- animate(src, pixel_x = pixel_x - pixel_x_diff, pixel_y = pixel_y - pixel_y_diff, transform = OM, time = 2)
+ var/matrix/initial_transform = matrix(transform)
+ var/matrix/rotated_transform = transform.Turn(15 * turn_dir)
+ animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, transform=rotated_transform, time = 1, easing=BACK_EASING|EASE_IN)
+ animate(pixel_x = pixel_x - pixel_x_diff, pixel_y = pixel_y - pixel_y_diff, transform=initial_transform, time = 2, easing=SINE_EASING)
/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item)
var/image/I
@@ -450,21 +530,21 @@
I.plane = GAME_PLANE
// Scale the icon.
- I.transform *= 0.75
+ I.transform *= 0.4
// The icon should not rotate.
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
// Set the direction of the icon animation.
var/direction = get_dir(src, A)
if(direction & NORTH)
- I.pixel_y = -16
+ I.pixel_y = -12
else if(direction & SOUTH)
- I.pixel_y = 16
+ I.pixel_y = 12
if(direction & EAST)
- I.pixel_x = -16
+ I.pixel_x = -14
else if(direction & WEST)
- I.pixel_x = 16
+ I.pixel_x = 14
if(!direction) // Attacked self?!
I.pixel_z = 16
@@ -472,10 +552,12 @@
if(!I)
return
- flick_overlay(I, GLOB.clients, 5) // 5 ticks/half a second
+ flick_overlay(I, GLOB.clients, 10)
// And animate the attack!
- animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3)
+ animate(I, alpha = 175, transform = matrix() * 0.75, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3)
+ animate(time = 1)
+ animate(alpha = 0, time = 3, easing = CIRCULAR_EASING|EASE_OUT)
/atom/movable/vv_get_dropdown()
. = ..()
@@ -492,19 +574,17 @@
return TRUE
//TODO: Better floating
-/atom/movable/proc/float(on)
- if(throwing)
+/atom/movable/proc/float(on, throw_override)
+ if(throwing || !throw_override)
return
- if(on && (!(movement_type & FLOATING) || floating_need_update))
- animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
- sleep(10)
- animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1)
- if(!(movement_type & FLOATING))
- setMovetype(movement_type | FLOATING)
- else if (!on && movement_type & FLOATING)
+ if(on && !(movement_type & FLOATING))
+ animate(src, pixel_y = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(pixel_y = -2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
+ setMovetype(movement_type | FLOATING)
+ else if (!on && (movement_type & FLOATING))
animate(src, pixel_y = initial(pixel_y), time = 10)
setMovetype(movement_type & ~FLOATING)
- floating_need_update = FALSE
+ floating_need_update = FALSE // assume it's done
/* Language procs
* Unless you are doing something very specific, these are the ones you want to use.
@@ -611,10 +691,32 @@
return FALSE
return TRUE
-/// Updates the grab state of the movable
-/// This exists to act as a hook for behaviour
+/**
+ * Updates the grab state of the movable
+ *
+ * This exists to act as a hook for behaviour
+ */
/atom/movable/proc/setGrabState(newstate)
+ if(newstate == grab_state)
+ return
+ // SEND_SIGNAL(src, COMSIG_MOVABLE_SET_GRAB_STATE, newstate)
+ . = grab_state
grab_state = newstate
+ // switch(grab_state) // Current state.
+ // if(GRAB_PASSIVE)
+ // REMOVE_TRAIT(pulling, TRAIT_IMMOBILIZED, CHOKEHOLD_TRAIT)
+ // REMOVE_TRAIT(pulling, TRAIT_HANDS_BLOCKED, CHOKEHOLD_TRAIT)
+ // if(. >= GRAB_NECK) // Previous state was a a neck-grab or higher.
+ // REMOVE_TRAIT(pulling, TRAIT_FLOORED, CHOKEHOLD_TRAIT)
+ // if(GRAB_AGGRESSIVE)
+ // if(. >= GRAB_NECK) // Grab got downgraded.
+ // REMOVE_TRAIT(pulling, TRAIT_FLOORED, CHOKEHOLD_TRAIT)
+ // else // Grab got upgraded from a passive one.
+ // ADD_TRAIT(pulling, TRAIT_IMMOBILIZED, CHOKEHOLD_TRAIT)
+ // ADD_TRAIT(pulling, TRAIT_HANDS_BLOCKED, CHOKEHOLD_TRAIT)
+ // if(GRAB_NECK, GRAB_KILL)
+ // if(. <= GRAB_AGGRESSIVE)
+ // ADD_TRAIT(pulling, TRAIT_FLOORED, CHOKEHOLD_TRAIT)
/obj/item/proc/do_pickup_animation(atom/target)
set waitfor = FALSE
@@ -626,31 +728,24 @@
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
var/turf/T = get_turf(src)
var/direction
- var/to_x = 0
- var/to_y = 0
+ var/to_x = initial(target.pixel_x)
+ var/to_y = initial(target.pixel_y)
if(!QDELETED(T) && !QDELETED(target))
direction = get_dir(T, target)
if(direction & NORTH)
- to_y = 32
+ to_y += 32
else if(direction & SOUTH)
- to_y = -32
+ to_y -= 32
if(direction & EAST)
- to_x = 32
+ to_x += 32
else if(direction & WEST)
- to_x = -32
+ to_x -= 32
if(!direction)
- to_y = 16
+ to_y += 16
flick_overlay(I, GLOB.clients, 6)
var/matrix/M = new
M.Turn(pick(-30, 30))
animate(I, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 3, transform = M, easing = CUBIC_EASING)
sleep(1)
animate(I, alpha = 0, transform = matrix(), time = 1)
-
-/atom/movable/proc/set_anchored(anchorvalue) //literally only for plumbing ran
- SHOULD_CALL_PARENT(TRUE)
- if(anchored == anchorvalue)
- return
- . = anchored
- anchored = anchorvalue
diff --git a/code/game/atoms_movement.dm b/code/game/atoms_movement.dm
index db9424d983..4f07ff6f95 100644
--- a/code/game/atoms_movement.dm
+++ b/code/game/atoms_movement.dm
@@ -5,8 +5,7 @@
// Here's where we rewrite how byond handles movement except slightly different
// To be removed on step_ conversion
// All this work to prevent a second bump
-/atom/movable/Move(atom/newloc, direct=0)
- set waitfor = FALSE //n o
+/atom/movable/Move(atom/newloc, direct=0, glide_size_override = 0)
. = FALSE
if(!newloc || newloc == loc)
return
@@ -52,8 +51,7 @@
//
////////////////////////////////////////
-/atom/movable/Move(atom/newloc, direct)
- set waitfor = FALSE //n o
+/atom/movable/Move(atom/newloc, direct, glide_size_override = 0)
var/atom/movable/pullee = pulling
var/turf/T = loc
if(!moving_from_pull)
@@ -61,6 +59,9 @@
if(!loc || !newloc)
return FALSE
var/atom/oldloc = loc
+ //Early override for some cases like diagonal movement
+ if(glide_size_override)
+ set_glide_size(glide_size_override, FALSE)
if(loc != newloc)
if (!(direct & (direct - 1))) //Cardinal move
@@ -120,32 +121,38 @@
return
if(!loc || (loc == oldloc && oldloc != newloc))
- last_move = NONE
+ last_move = 0
return
+ setDir(direct)
if(.)
- last_move = direct
- setDir(direct)
-
- if(has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
- return FALSE
-
- if(pulling && pulling == pullee && pulling != moving_from_pull) //we were pulling a thing and didn't lose it during our move.
- if(pulling.anchored)
- stop_pulling()
- else
- var/pull_dir = get_dir(src, pulling)
- //puller and pullee more than one tile away or in diagonal position
- if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir)))
- pulling.moving_from_pull = src
- pulling.Move(T, get_dir(pulling, T)) //the pullee tries to reach our previous position
- pulling.moving_from_pull = null
Moved(oldloc, direct)
+ if(. && pulling && pulling == pullee && pulling != moving_from_pull) //we were pulling a thing and didn't lose it during our move.
+ if(pulling.anchored)
+ stop_pulling()
+ else
+ var/pull_dir = get_dir(src, pulling)
+ //puller and pullee more than one tile away or in diagonal position
+ if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir)))
+ pulling.moving_from_pull = src
+ pulling.Move(T, get_dir(pulling, T), glide_size) //the pullee tries to reach our previous position
+ pulling.moving_from_pull = null
+ check_pulling()
-/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
+
+ //glide_size strangely enough can change mid movement animation and update correctly while the animation is playing
+ //This means that if you don't override it late like this, it will just be set back by the movement update that's called when you move turfs.
+ if(glide_size_override)
+ set_glide_size(glide_size_override, FALSE)
+
+ last_move = direct
+ if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob(s)
+ return FALSE
+
+/atom/movable/proc/handle_buckled_mob_movement(newloc, direct, glide_size_override)
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
- if(!buckled_mob.Move(newloc, direct))
+ if(!buckled_mob.Move(newloc, direct, glide_size_override))
forceMove(buckled_mob.loc)
last_move = buckled_mob.last_move
inertia_dir = last_move
@@ -155,6 +162,7 @@
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE)
+ SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced)
if (!inertia_moving)
inertia_next_move = world.time + inertia_move_delay
@@ -174,6 +182,8 @@
//oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called!
/atom/movable/Crossed(atom/movable/AM, oldloc)
+ // SHOULD_CALL_PARENT(TRUE)
+ . = ..()
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM)
/atom/movable/Uncross(atom/movable/AM, atom/newloc)
@@ -204,7 +214,11 @@
var/atom/movable/AM = item
AM.onTransitZ(old_z,new_z)
+///Proc to modify the movement_type and hook behavior associated with it changing.
/atom/movable/proc/setMovetype(newval)
+ if(movement_type == newval)
+ return
+ . = movement_type
movement_type = newval
///////////// FORCED MOVEMENT /////////////
@@ -268,37 +282,44 @@
old_area.Exited(src, null)
loc = null
-//Called whenever an object moves and by mobs when they attempt to move themselves through space
-//And when an object or action applies a force on src, see newtonian_move() below
-//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting
-//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm
-//movement_dir == 0 when stopping or any dir when trying to move
+/**
+ * Called whenever an object moves and by mobs when they attempt to move themselves through space
+ * And when an object or action applies a force on src, see [newtonian_move][/atom/movable/proc/newtonian_move]
+ *
+ * Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting
+ *
+ * Mobs should return 1 if they should be able to move of their own volition, see [/client/proc/Move]
+ *
+ * Arguments:
+ * * movement_dir - 0 when stopping or any dir when trying to move
+ */
/atom/movable/proc/Process_Spacemove(movement_dir = 0)
if(has_gravity(src))
- return 1
+ return TRUE
- if(pulledby)
- return 1
+ if(pulledby && (pulledby.pulledby != src || moving_from_pull))
+ return TRUE
if(throwing)
- return 1
+ return TRUE
if(!isturf(loc))
- return 1
+ return TRUE
if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier
- return 1
+ return TRUE
- return 0
+ return FALSE
-/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity
- if(!loc || Process_Spacemove(0))
+/// Only moves the object if it's under no gravity
+/atom/movable/proc/newtonian_move(direction)
+ if(!isturf(loc) || Process_Spacemove(0))
inertia_dir = 0
- return 0
+ return FALSE
inertia_dir = direction
if(!direction)
- return 1
+ return TRUE
inertia_last_loc = loc
SSspacedrift.processing[src] = src
- return 1
+ return TRUE
diff --git a/code/game/gamemodes/gangs/gang_items.dm b/code/game/gamemodes/gangs/gang_items.dm
index 7d6ecd00f6..d1cf006600 100644
--- a/code/game/gamemodes/gangs/gang_items.dm
+++ b/code/game/gamemodes/gangs/gang_items.dm
@@ -133,7 +133,7 @@
icon_state = "knuckles"
w_class = 3
-datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang member anyways?
+/datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang member anyways?
name = "Cool Sunglasses"
id = "glasses"
cost = 5
@@ -313,13 +313,13 @@ datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang m
permeability_coefficient = 0.01
clothing_flags = NOSLIP
-datum/gang_item/equipment/shield
+/datum/gang_item/equipment/shield
name = "Riot Shield"
id = "riot_shield"
cost = 25
item_path = /obj/item/shield/riot
-datum/gang_item/equipment/gangsheild
+/datum/gang_item/equipment/gangsheild
name = "Tower Shield"
id = "metal"
cost = 45 //High block of melee and even higher for bullets
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index d8617e0b48..0ee07951a9 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -455,7 +455,7 @@ If not set, defaults to check_completion instead. Set it. It's used by cryo.
var/target_real_name // Has to be stored because the target's real_name can change over the course of the round
var/target_missing_id
-/datum/objective/escape/escape_with_identity/find_target()
+/datum/objective/escape/escape_with_identity/find_target(dupe_search_range, blacklist)
target = ..()
update_explanation_text()
@@ -553,7 +553,7 @@ GLOBAL_LIST_EMPTY(possible_items)
for(var/I in subtypesof(/datum/objective_item/steal))
new I
-/datum/objective/steal/find_target()
+/datum/objective/steal/find_target(dupe_search_range, blacklist)
var/list/datum/mind/owners = get_owners()
var/approved_targets = list()
check_items:
@@ -631,7 +631,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
for(var/I in subtypesof(/datum/objective_item/special) + subtypesof(/datum/objective_item/stack))
new I
-/datum/objective/steal/special/find_target()
+/datum/objective/steal/special/find_target(dupe_search_range, blacklist)
return set_target(pick(GLOB.possible_items_special))
/datum/objective/steal/exchange
@@ -844,7 +844,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
name = "destroy AI"
martyr_compatible = 1
-/datum/objective/destroy/find_target()
+/datum/objective/destroy/find_target(dupe_search_range, blacklist)
var/list/possible_targets = active_ais(1)
var/mob/living/silicon/ai/target_ai = pick(possible_targets)
target = target_ai.mind
@@ -1124,7 +1124,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
/datum/objective/hoard/heirloom
name = "steal heirloom"
-/datum/objective/hoard/heirloom/find_target()
+/datum/objective/hoard/heirloom/find_target(dupe_search_range, blacklist)
set_target(pick(GLOB.family_heirlooms))
GLOBAL_LIST_EMPTY(traitor_contraband)
@@ -1141,7 +1141,7 @@ GLOBAL_LIST_EMPTY(cult_contraband)
if(!GLOB.cult_contraband.len)
GLOB.cult_contraband = list(/obj/item/clockwork/slab,/obj/item/clockwork/component/belligerent_eye,/obj/item/clockwork/component/belligerent_eye/lens_gem,/obj/item/shuttle_curse,/obj/item/cult_shift)
-/datum/objective/hoard/collector/find_target()
+/datum/objective/hoard/collector/find_target(dupe_search_range, blacklist)
var/obj/item/I
var/I_type
if(prob(50))
@@ -1172,7 +1172,7 @@ GLOBAL_LIST_EMPTY(possible_sabotages)
for(var/I in subtypesof(/datum/sabotage_objective))
new I
-/datum/objective/sabotage/find_target()
+/datum/objective/sabotage/find_target(dupe_search_range, blacklist)
var/list/datum/mind/owners = get_owners()
var/approved_targets = list()
check_sabotages:
diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm
index e515f56196..af5093d6ca 100644
--- a/code/game/machinery/camera/motion.dm
+++ b/code/game/machinery/camera/motion.dm
@@ -109,4 +109,4 @@
else if (world.time > detectTime)
detectTime = 0
for(var/obj/machinery/computer/security/telescreen/entertainment/TV in GLOB.machines)
- TV.notify(FALSE)
\ No newline at end of file
+ TV.notify(FALSE)
diff --git a/code/game/machinery/colormate.dm b/code/game/machinery/colormate.dm
index b4306d49a4..1f1c16248c 100644
--- a/code/game/machinery/colormate.dm
+++ b/code/game/machinery/colormate.dm
@@ -6,10 +6,12 @@
density = TRUE
anchored = TRUE
circuit = /obj/item/circuitboard/machine/colormate
- var/obj/item/inserted
+ var/atom/movable/inserted
var/activecolor = "#FFFFFF"
var/list/color_matrix_last
var/matrix_mode = FALSE
+ /// Allow holder'd mobs
+ var/allow_mobs = TRUE
/// Minimum lightness for normal mode
var/minimum_normal_lightness = 50
/// Minimum lightness for matrix mode, tested using 4 test colors of full red, green, blue, white.
@@ -57,11 +59,22 @@
return
if(user.a_intent == INTENT_HARM)
return ..()
+ if(allow_mobs && istype(I, /obj/item/clothing/head/mob_holder))
+ var/obj/item/clothing/head/mob_holder/H = I
+ var/mob/victim = H.held_mob
+ if(!user.transferItemToLoc(I, src))
+ to_chat(user, "[I] is stuck to your hand!")
+ return
+ if(!QDELETED(H))
+ H.release()
+ insert_mob(victim, user)
if(is_type_in_list(I, allowed_types) && is_operational())
if(!user.transferItemToLoc(I, src))
to_chat(user, "[I] is stuck to your hand!")
return
+ if(QDELETED(I))
+ return
user.visible_message("[user] inserts [I] into [src]'s receptable.")
inserted = I
@@ -69,9 +82,22 @@
else
return ..()
+/obj/machinery/gear_painter/proc/insert_mob(mob/victim, mob/user)
+ if(inserted)
+ return
+ if(user)
+ visible_message("[user] stuffs [victim] into [src]!")
+ inserted = victim
+ inserted.forceMove(src)
+
/obj/machinery/gear_painter/AllowDrop()
return FALSE
+/obj/machinery/gear_painter/handle_atom_del(atom/movable/AM)
+ if(AM == inserted)
+ inserted = null
+ return ..()
+
/obj/machinery/gear_painter/AltClick(mob/user)
. = ..()
if(!user.CanReach(src))
diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm
index 5a0f0e9acf..96f224f4cf 100644
--- a/code/game/machinery/computer/arcade/battle.dm
+++ b/code/game/machinery/computer/arcade/battle.dm
@@ -60,7 +60,7 @@
blocked = TRUE
var/attackamt = rand(2,6)
temp = "You attack for [attackamt] damage!"
- playsound(loc, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)
updateUsrDialog()
if(turtle > 0)
turtle--
@@ -74,7 +74,7 @@
var/pointamt = rand(1,3)
var/healamt = rand(6,8)
temp = "You use [pointamt] magic to heal for [healamt] damage!"
- playsound(loc, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)
updateUsrDialog()
turtle++
@@ -89,7 +89,7 @@
blocked = TRUE
var/chargeamt = rand(4,7)
temp = "You regain [chargeamt] points"
- playsound(loc, 'sound/arcade/mana.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)
player_mp += chargeamt
if(turtle > 0)
turtle--
@@ -124,7 +124,7 @@
if(!gameover)
gameover = TRUE
temp = "[enemy_name] has fallen! Rejoice!"
- playsound(loc, 'sound/arcade/win.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3)
if(obj_flags & EMAGGED)
new /obj/effect/spawner/newbomb/timer/syndicate(loc)
@@ -141,13 +141,13 @@
else if ((obj_flags & EMAGGED) && (turtle >= 4))
var/boomamt = rand(5,10)
temp = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!"
- playsound(loc, 'sound/arcade/boom.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/boom.ogg', 50, TRUE, extrarange = -3)
player_hp -= boomamt
else if ((enemy_mp <= 5) && (prob(70)))
var/stealamt = rand(2,3)
temp = "[enemy_name] steals [stealamt] of your power!"
- playsound(loc, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3)
player_mp -= stealamt
updateUsrDialog()
@@ -155,27 +155,27 @@
gameover = TRUE
sleep(turn_speed)
temp = "You have been drained! GAME OVER"
- playsound(loc, 'sound/arcade/lose.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3)
if(obj_flags & EMAGGED)
usr.gib()
SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "mana", (obj_flags & EMAGGED ? "emagged":"normal")))
else if ((enemy_hp <= 10) && (enemy_mp > 4))
temp = "[enemy_name] heals for 4 health!"
- playsound(loc, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)
enemy_hp += 4
enemy_mp -= 4
else
var/attackamt = rand(3,6)
temp = "[enemy_name] attacks for [attackamt] damage!"
- playsound(loc, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)
player_hp -= attackamt
if ((player_mp <= 0) || (player_hp <= 0))
gameover = TRUE
temp = "You have been crushed! GAME OVER"
- playsound(loc, 'sound/arcade/lose.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3)
if(obj_flags & EMAGGED)
usr.gib()
SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "hp", (obj_flags & EMAGGED ? "emagged":"normal")))
diff --git a/code/game/machinery/computer/arcade/minesweeper.dm b/code/game/machinery/computer/arcade/minesweeper.dm
index a9c9b5dfd9..3f7ef778b0 100644
--- a/code/game/machinery/computer/arcade/minesweeper.dm
+++ b/code/game/machinery/computer/arcade/minesweeper.dm
@@ -78,7 +78,7 @@
columns = 0
mine_placed = 0
if(href_list["Easy"])
- playsound(loc, startup_sound, 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, startup_sound, 50, FALSE, extrarange = -3)
flag_text = "OFF"
game_status = MINESWEEPER_GAME_PLAYING
reset_board = TRUE
@@ -87,7 +87,7 @@
columns = 10
mine_limit = 10
if(href_list["Intermediate"])
- playsound(loc, startup_sound, 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, startup_sound, 50, FALSE, extrarange = -3)
flag_text = "OFF"
game_status = MINESWEEPER_GAME_PLAYING
reset_board = TRUE
@@ -96,7 +96,7 @@
columns = 17
mine_limit = 40
if(href_list["Hard"])
- playsound(loc, startup_sound, 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, startup_sound, 50, FALSE, extrarange = -3)
flag_text = "OFF"
game_status = MINESWEEPER_GAME_PLAYING
reset_board = TRUE
@@ -110,9 +110,9 @@
game_status = MINESWEEPER_GAME_PLAYING
reset_board = TRUE
difficulty = "Custom"
- playsound(loc, startup_sound, 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, startup_sound, 50, FALSE, extrarange = -3)
if(href_list["Flag"])
- playsound(loc, 'sound/arcade/minesweeper_boardpress.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_boardpress.ogg', 50, FALSE, extrarange = -3)
if(!flagging)
flagging = TRUE
flag_text = "ON"
@@ -122,10 +122,10 @@
if(game_status == MINESWEEPER_GAME_MAIN_MENU)
if(CHECK_BITFIELD(obj_flags, EMAGGED))
- playsound(loc, 'sound/arcade/minesweeper_emag2.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_emag2.ogg', 50, FALSE, extrarange = -3)
web += "Explode in the game, explode in real life!
What difficulty do you want to play?
Easy (9x9 board, 10 mines)
Intermediate (16x16 board, 40 mines)
Hard (16x30 board, 99 mines)
Custom"
else
- playsound(loc, 'sound/arcade/minesweeper_startup.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_startup.ogg', 50, FALSE, extrarange = -3)
web += web_difficulty_menu
if(game_status == MINESWEEPER_GAME_PLAYING)
@@ -152,7 +152,7 @@
if(table[y1][x1] < 10 && table[y1][x1] >= 0) //Check that it's not already revealed, and stop flag removal if we're out of flag mode
table[y1][x1] += 10
if(table[y1][x1] != 10)
- playsound(loc, 'sound/arcade/minesweeper_boardpress.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_boardpress.ogg', 50, FALSE, extrarange = -3)
else
if(game_status != MINESWEEPER_GAME_LOST && game_status != MINESWEEPER_GAME_WON)
game_status = MINESWEEPER_GAME_LOST
@@ -164,14 +164,14 @@
if(mine_sound)
switch(rand(1,3)) //Play every time a mine is hit
if(1)
- playsound(loc, 'sound/arcade/minesweeper_explosion1.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_explosion1.ogg', 50, FALSE, extrarange = -3)
if(2)
- playsound(loc, 'sound/arcade/minesweeper_explosion2.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_explosion2.ogg', 50, FALSE, extrarange = -3)
if(3)
- playsound(loc, 'sound/arcade/minesweeper_explosion3.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_explosion3.ogg', 50, FALSE, extrarange = -3)
mine_sound = FALSE
else
- playsound(loc, 'sound/arcade/minesweeper_boardpress.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_boardpress.ogg', 50, FALSE, extrarange = -3)
if(table[y1][x1] >= 0) //Check that it's not already flagged
table[y1][x1] -= 10
else if(table[y1][x1] < 0) //If flagged, remove the flag
@@ -182,7 +182,7 @@
game_status = MINESWEEPER_GAME_PLAYING
if(table[y1][x1] >= 10) //If revealed, become unrevealed!
if(mine_sound)
- playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, FALSE, extrarange = -3)
mine_sound = FALSE
table[y1][x1] -= 10
if(table[y1][x1] > 10 && !reset_board)
@@ -243,10 +243,10 @@
if(safe_squares_revealed >= win_condition && game_status == MINESWEEPER_GAME_PLAYING)
game_status = MINESWEEPER_GAME_WON
if(rows < 10 || columns < 10) //If less than easy difficulty
- playsound(loc, 'sound/arcade/minesweeper_winfail.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_winfail.ogg', 50, FALSE, extrarange = -3)
say("You cleared the board of all mines, but you picked too small of a board! Try again with at least a 9x9 board!")
else
- playsound(loc, 'sound/arcade/minesweeper_win.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_win.ogg', 50, FALSE, extrarange = -3)
say("You cleared the board of all mines! Congratulations!")
if(CHECK_BITFIELD(obj_flags, EMAGGED))
var/itemname
@@ -299,32 +299,32 @@
ENABLE_BITFIELD(obj_flags, EMAGGED)
if(game_status == MINESWEEPER_GAME_MAIN_MENU)
to_chat(user, "An ominous tune plays from the arcade's speakers!")
- playsound(user, 'sound/arcade/minesweeper_emag1.ogg', 100, 0, extrarange = 3, falloff = 10)
+ playsound(user, 'sound/arcade/minesweeper_emag1.ogg', 100, FALSE, extrarange = 3)
else //Can't let you do that, star fox!
to_chat(user, "The machine buzzes and sparks... the game has been reset!")
- playsound(user, 'sound/machines/buzz-sigh.ogg', 100, 0, extrarange = 3, falloff = 10) //Loud buzz
+ playsound(user, 'sound/machines/buzz-sigh.ogg', 100, FALSE, extrarange = 3) //Loud buzz
game_status = MINESWEEPER_GAME_MAIN_MENU
return TRUE
/obj/machinery/computer/arcade/minesweeper/proc/custom_generation(mob/user)
- playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10) //Entered into the menu so ping sound
+ playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, FALSE, extrarange = -3) //Entered into the menu so ping sound
var/new_rows = input(user, "How many rows do you want? (Minimum: 4, Maximum: 30)", "Minesweeper Rows") as null|num
if(!new_rows || !user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return FALSE
new_rows = clamp(new_rows + 1, 4, 30)
- playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, FALSE, extrarange = -3)
var/new_columns = input(user, "How many columns do you want? (Minimum: 4, Maximum: 50)", "Minesweeper Squares") as null|num
if(!new_columns || !user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return FALSE
new_columns = clamp(new_columns + 1, 4, 50)
- playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, FALSE, extrarange = -3)
var/grid_area = (new_rows - 1) * (new_columns - 1)
var/lower_limit = round(grid_area*0.156)
var/upper_limit = round(grid_area*0.85)
var/new_mine_limit = input(user, "How many mines do you want? (Minimum: [lower_limit], Maximum: [upper_limit])", "Minesweeper Mines") as null|num
if(!new_mine_limit || !user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return FALSE
- playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, FALSE, extrarange = -3)
rows = new_rows
columns = new_columns
mine_limit = clamp(new_mine_limit, lower_limit, upper_limit)
diff --git a/code/game/machinery/computer/arcade/misc_arcade.dm b/code/game/machinery/computer/arcade/misc_arcade.dm
index 24516740f9..50633192ce 100644
--- a/code/game/machinery/computer/arcade/misc_arcade.dm
+++ b/code/game/machinery/computer/arcade/misc_arcade.dm
@@ -17,14 +17,14 @@
to_chat(c_user, "You move your hand towards the machine, and begin to hesitate as a bloodied guillotine emerges from inside of it...")
if(do_after(c_user, 50, target = src))
to_chat(c_user, "The guillotine drops on your arm, and the machine sucks it in!")
- playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
+ playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
var/which_hand = BODY_ZONE_L_ARM
if(!(c_user.active_hand_index % 2))
which_hand = BODY_ZONE_R_ARM
var/obj/item/bodypart/chopchop = c_user.get_bodypart(which_hand)
chopchop.dismember()
qdel(chopchop)
- playsound(loc, 'sound/arcade/win.ogg', 50, 1, extrarange = -3, falloff = 10)
+ playsound(loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3)
for(var/i=1; i<=rand(3,5); i++)
prizevend(user)
else
diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm
index a563200575..fa516f4b6b 100644
--- a/code/game/machinery/doors/alarmlock.dm
+++ b/code/game/machinery/doors/alarmlock.dm
@@ -23,7 +23,7 @@
. = ..()
SSradio.remove_object(src, air_frequency)
air_connection = SSradio.add_object(src, air_frequency, RADIO_TO_AIRALARM)
- open()
+ INVOKE_ASYNC(src, .proc/open)
/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal)
..()
diff --git a/code/game/machinery/quantum_pad.dm b/code/game/machinery/quantum_pad.dm
index f9fda50daf..4f706954a6 100644
--- a/code/game/machinery/quantum_pad.dm
+++ b/code/game/machinery/quantum_pad.dm
@@ -130,7 +130,7 @@
/obj/machinery/quantumpad/proc/doteleport(mob/user, obj/machinery/quantumpad/target_pad = linked_pad)
if(target_pad)
- playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, 1)
+ playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, TRUE)
teleporting = TRUE
spawn(teleport_speed)
@@ -155,9 +155,9 @@
target_pad.sparks()
flick("qpad-beam", src)
- playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 25, 1, extrarange = 3, falloff = 5)
+ playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 25, TRUE)
flick("qpad-beam", target_pad)
- playsound(get_turf(target_pad), 'sound/weapons/emitter2.ogg', 25, 1, extrarange = 3, falloff = 5)
+ playsound(get_turf(target_pad), 'sound/weapons/emitter2.ogg', 25, TRUE)
for(var/atom/movable/ROI in get_turf(src))
if(QDELETED(ROI))
continue //sleeps in CHECK_TICK
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 706d2bb9bf..2ffd556dc1 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -100,51 +100,54 @@
eat(AM)
. = ..()
-/obj/machinery/recycler/proc/eat(atom/AM0)
- if(stat & (BROKEN|NOPOWER) || safety_mode)
+/obj/machinery/recycler/proc/eat(atom/movable/AM0, sound=TRUE)
+ if(stat & (BROKEN|NOPOWER))
return
+ if(safety_mode)
+ return
+ if(!isturf(AM0.loc))
+ return //I don't know how you called Crossed() but stop it.
- var/list/to_eat
+ var/list/to_eat = AM0.GetAllContents()
- to_eat = list(AM0)
+ var/living_detected = FALSE //technically includes silicons as well but eh
+ var/list/nom = list()
+ var/list/crunchy_nom = list() //Mobs have to be handled differently so they get a different list instead of checking them multiple times.
- var/items_recycled = 0
- var/buzz = FALSE
for(var/i in to_eat)
var/atom/movable/AM = i
- if(QDELETED(AM))
- continue
- var/obj/item/bodypart/head/as_head = AM
- var/obj/item/mmi/as_mmi = AM
- var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(AM, /obj/item/dullahan_relay)
- if(brain_holder)
- if(obj_flags & EMAGGED)
- continue
- else
- emergency_stop(AM)
- return
+ if(istype(AM, /obj/item))
+ var/obj/item/bodypart/head/as_head = AM
+ var/obj/item/mmi/as_mmi = AM
+ if(istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(AM, /obj/item/dullahan_relay))
+ living_detected = TRUE
+ nom += AM
else if(isliving(AM))
- if((obj_flags & EMAGGED)||((!allowed(AM))&&(!ishuman(AM))))
- to_eat += crush_living(AM)
- else
- emergency_stop(AM)
- return
- else if(isitem(AM))
- var/obj/O = AM
- if(O.resistance_flags & INDESTRUCTIBLE)
- buzz = TRUE
- O.forceMove(loc)
- else
- to_eat += recycle_item(AM)
- items_recycled++
- else
- buzz = TRUE
- AM.forceMove(loc)
-
- if(items_recycled)
- playsound(src, item_recycle_sound, 50, 1)
- if(buzz)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
+ living_detected = TRUE
+ crunchy_nom += AM
+ var/not_eaten = to_eat.len - nom.len - crunchy_nom.len
+ if(living_detected) // First, check if we have any living beings detected.
+ if(obj_flags & EMAGGED)
+ for(var/CRUNCH in crunchy_nom) // Eat them and keep going because we don't care about safety.
+ if(isliving(CRUNCH)) // MMIs and brains will get eaten like normal items
+ crush_living(CRUNCH)
+ else // Stop processing right now without eating anything.
+ emergency_stop()
+ return
+ for(var/nommed in nom)
+ recycle_item(nommed)
+ if(nom.len && sound)
+ playsound(src, item_recycle_sound, (50 + nom.len*5), TRUE, nom.len, ignore_walls = (nom.len - 10)) // As a substitute for playing 50 sounds at once.
+ if(not_eaten)
+ playsound(src, 'sound/machines/buzz-sigh.ogg', (50 + not_eaten*5), FALSE, not_eaten, ignore_walls = (not_eaten - 10)) // Ditto.
+ if(!ismob(AM0))
+ AM0.moveToNullspace()
+ qdel(AM0)
+ else // Lets not move a mob to nullspace and qdel it, yes?
+ for(var/i in AM0.contents)
+ var/atom/movable/content = i
+ content.moveToNullspace()
+ qdel(content)
/obj/machinery/recycler/proc/recycle_item(obj/item/I)
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 6282d74346..1fadad168e 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -11,6 +11,8 @@
#define SPIN_TIME 65 //As always, deciseconds.
#define REEL_DEACTIVATE_DELAY 7
#define SEVEN "7"
+#define HOLOCHIP 1
+#define COIN 2
/obj/machinery/computer/slot_machine
name = "slot machine"
@@ -21,40 +23,48 @@
use_power = IDLE_POWER_USE
idle_power_usage = 50
circuit = /obj/item/circuitboard/computer/slot_machine
+ light_color = LIGHT_COLOR_BROWN
var/money = 3000 //How much money it has CONSUMED
var/plays = 0
- var/working = 0
+ var/working = FALSE
var/balance = 0 //How much money is in the machine, ready to be CONSUMED.
var/jackpots = 0
+ var/paymode = HOLOCHIP //toggles between HOLOCHIP/COIN, defined above
+ var/cointype = /obj/item/coin/iron //default cointype
+ var/list/coinvalues = list()
var/list/reels = list(list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0)
var/list/symbols = list(SEVEN = 1, "&" = 2, "@" = 2, "$" = 2, "?" = 2, "#" = 2, "!" = 2, "%" = 2) //if people are winning too much, multiply every number in this list by 2 and see if they are still winning too much.
- light_color = LIGHT_COLOR_BROWN
/obj/machinery/computer/slot_machine/Initialize()
. = ..()
jackpots = rand(1, 4) //false hope
plays = rand(75, 200)
- toggle_reel_spin(1) //The reels won't spin unless we activate them
+ INVOKE_ASYNC(src, .proc/toggle_reel_spin, TRUE)//The reels won't spin unless we activate them
var/list/reel = reels[1]
for(var/i = 0, i < reel.len, i++) //Populate the reels.
randomize_reels()
- toggle_reel_spin(0)
+ INVOKE_ASYNC(src, .proc/toggle_reel_spin, FALSE)
+
+ for(cointype in typesof(/obj/item/coin))
+ var/obj/item/coin/C = new cointype
+ coinvalues["[cointype]"] = C.get_item_credit_value()
+ qdel(C) //Sigh
/obj/machinery/computer/slot_machine/Destroy()
if(balance)
- give_coins(balance)
+ give_payout(balance)
return ..()
-/obj/machinery/computer/slot_machine/process()
+/obj/machinery/computer/slot_machine/process(delta_time)
. = ..() //Sanity checks.
if(!.)
return .
- money++ //SPESSH MAJICKS
+ money += round(delta_time / 2) //SPESSH MAJICKS
/obj/machinery/computer/slot_machine/update_icon_state()
if(stat & NOPOWER)
@@ -69,27 +79,46 @@
else
icon_state = "slots1"
-/obj/machinery/computer/slot_machine/power_change()
- ..()
- update_icon()
-
/obj/machinery/computer/slot_machine/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/coin))
var/obj/item/coin/C = I
- if(prob(2))
- if(!user.transferItemToLoc(C, drop_location()))
- return
- C.throw_at(user, 3, 10)
- if(prob(10))
- balance = max(balance - SPIN_PRICE, 0)
- to_chat(user, "[src] spits your coin back out!")
+ if(paymode == COIN)
+ if(prob(2))
+ if(!user.transferItemToLoc(C, drop_location()))
+ return
+ C.throw_at(user, 3, 10)
+ if(prob(10))
+ balance = max(balance - SPIN_PRICE, 0)
+ to_chat(user, "[src] spits your coin back out!")
+ else
+ if(!user.temporarilyRemoveItemFromInventory(C))
+ return
+ to_chat(user, "You insert [C] into [src]'s slot!")
+ balance += C.value
+ qdel(C)
else
- if(!user.temporarilyRemoveItemFromInventory(C))
+ to_chat(user, "This machine is only accepting holochips!")
+ else if(istype(I, /obj/item/holochip))
+ if(paymode == HOLOCHIP)
+ var/obj/item/holochip/H = I
+ if(!user.temporarilyRemoveItemFromInventory(H))
return
- to_chat(user, "You insert [C] into [src]'s slot!")
- balance += C.value
- qdel(C)
+ to_chat(user, "You insert [H.credits] holocredits into [src]'s slot!")
+ balance += H.credits
+ qdel(H)
+ else
+ to_chat(user, "This machine is only accepting coins!")
+ else if(I.tool_behaviour == TOOL_MULTITOOL)
+ if(balance > 0)
+ visible_message("[src] says, 'ERROR! Please empty the machine balance before altering paymode'") //Prevents converting coins into holocredits and vice versa
+ else
+ if(paymode == HOLOCHIP)
+ paymode = COIN
+ visible_message("[src] says, 'This machine now works with COINS!'")
+ else
+ paymode = HOLOCHIP
+ visible_message("[src] says, 'This machine now works with HOLOCHIPS!'")
else
return ..()
@@ -101,8 +130,7 @@
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(4, 0, src.loc)
spark_system.start()
- playsound(src, "sparks", 50, 1)
- return TRUE
+ playsound(src, "sparks", 50, TRUE)
/obj/machinery/computer/slot_machine/ui_interact(mob/living/user)
. = ..()
@@ -127,8 +155,9 @@
Play!
[reeltext]
-
- Refund balance
"}
+
"}
+ if(balance > 0)
+ dat+="Refund balance
"
var/datum/browser/popup = new(user, "slotmachine", "Slot Machine")
popup.set_content(dat)
@@ -143,21 +172,22 @@
spin(usr)
else if(href_list["refund"])
- give_coins(balance)
- balance = 0
+ if(balance > 0)
+ give_payout(balance)
+ balance = 0
/obj/machinery/computer/slot_machine/emp_act(severity)
. = ..()
if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
return
- if(prob(1500 / severity))
+ if(prob(15 * severity))
return
- if(prob(1 * severity/100)) // :^)
+ if(prob(1)) // :^)
obj_flags |= EMAGGED
var/severity_ascending = 4 - severity
money = max(rand(money - (200 * severity_ascending), money + (200 * severity_ascending)), 0)
balance = max(rand(balance - (50 * severity_ascending), balance + (50 * severity_ascending)), 0)
- money -= max(0, give_coins(min(rand(-50, 100 * severity_ascending)), money)) //This starts at -50 because it shouldn't always dispense coins yo
+ money -= max(0, give_payout(min(rand(-50, 100 * severity_ascending)), money)) //This starts at -50 because it shouldn't always dispense coins yo
spin()
/obj/machinery/computer/slot_machine/proc/spin(mob/user)
@@ -174,37 +204,43 @@
balance -= SPIN_PRICE
money += SPIN_PRICE
plays += 1
- working = 1
+ working = TRUE
toggle_reel_spin(1)
update_icon()
updateDialog()
- spawn(0)
- while(working)
- randomize_reels()
- updateDialog()
- sleep(2)
+ var/spin_loop = addtimer(CALLBACK(src, .proc/do_spin), 2, TIMER_LOOP|TIMER_STOPPABLE)
- spawn(SPIN_TIME - (REEL_DEACTIVATE_DELAY * reels.len)) //WARNING: no sanity checking for user since it's not needed and would complicate things (machine should still spin even if user is gone), be wary of this if you're changing this code.
- toggle_reel_spin(0, REEL_DEACTIVATE_DELAY)
- working = 0
- give_prizes(the_name, user)
- update_icon()
- updateDialog()
+ addtimer(CALLBACK(src, .proc/finish_spinning, spin_loop, user, the_name), SPIN_TIME - (REEL_DEACTIVATE_DELAY * reels.len))
+ //WARNING: no sanity checking for user since it's not needed and would complicate things (machine should still spin even if user is gone), be wary of this if you're changing this code.
+
+/obj/machinery/computer/slot_machine/proc/do_spin()
+ randomize_reels()
+ updateDialog()
+
+/obj/machinery/computer/slot_machine/proc/finish_spinning(spin_loop, mob/user, the_name)
+ toggle_reel_spin(0, REEL_DEACTIVATE_DELAY)
+ working = FALSE
+ deltimer(spin_loop)
+ give_prizes(the_name, user)
+ update_icon()
+ updateDialog()
/obj/machinery/computer/slot_machine/proc/can_spin(mob/user)
if(stat & NOPOWER)
to_chat(user, "The slot machine has no power!")
+ return FALSE
if(stat & BROKEN)
to_chat(user, "The slot machine is broken!")
+ return FALSE
if(working)
to_chat(user, "You need to wait until the machine stops spinning before you can play again!")
- return 0
+ return FALSE
if(balance < SPIN_PRICE)
to_chat(user, "Insufficient money to play!")
- return 0
- return 1
+ return FALSE
+ return TRUE
/obj/machinery/computer/slot_machine/proc/toggle_reel_spin(value, delay = 0) //value is 1 or 0 aka on or off
for(var/list/reel in reels)
@@ -223,23 +259,25 @@
var/linelength = get_lines()
if(reels[1][2] + reels[2][2] + reels[3][2] + reels[4][2] + reels[5][2] == "[SEVEN][SEVEN][SEVEN][SEVEN][SEVEN]")
- visible_message("[src] says, 'JACKPOT! You win [money] credits worth of coins!'")
+ visible_message("[src] says, 'JACKPOT! You win [money] credits!'")
priority_announce("Congratulations to [user ? user.real_name : usrname] for winning the jackpot at the slot machine in [get_area(src)]!")
jackpots += 1
- balance += money - give_coins(JACKPOT)
+ balance += money - give_payout(JACKPOT)
money = 0
-
- for(var/i = 0, i < 5, i++)
- var/cointype = pick(subtypesof(/obj/item/coin))
- var/obj/item/coin/C = new cointype(loc)
- random_step(C, 2, 50)
+ if(paymode == HOLOCHIP)
+ new /obj/item/holochip(loc,JACKPOT)
+ else
+ for(var/i = 0, i < 5, i++)
+ cointype = pick(subtypesof(/obj/item/coin))
+ var/obj/item/coin/C = new cointype(loc)
+ random_step(C, 2, 50)
else if(linelength == 5)
- visible_message("[src] says, 'Big Winner! You win a thousand credits worth of coins!'")
+ visible_message("[src] says, 'Big Winner! You win a thousand credits!'")
give_money(BIG_PRIZE)
else if(linelength == 4)
- visible_message("[src] says, 'Winner! You win four hundred credits worth of coins!'")
+ visible_message("[src] says, 'Winner! You win four hundred credits!'")
give_money(SMALL_PRIZE)
else if(linelength == 3)
@@ -271,12 +309,15 @@
/obj/machinery/computer/slot_machine/proc/give_money(amount)
var/amount_to_give = money >= amount ? amount : money
- var/surplus = amount_to_give - give_coins(amount_to_give)
+ var/surplus = amount_to_give - give_payout(amount_to_give)
money = max(0, money - amount)
balance += surplus
-/obj/machinery/computer/slot_machine/proc/give_coins(amount)
- var/cointype = obj_flags & EMAGGED ? /obj/item/coin/iron : /obj/item/coin/silver
+/obj/machinery/computer/slot_machine/proc/give_payout(amount)
+ if(paymode == HOLOCHIP)
+ cointype = /obj/item/holochip
+ else
+ cointype = obj_flags & EMAGGED ? /obj/item/coin/iron : /obj/item/coin/silver
if(!(obj_flags & EMAGGED))
amount = dispense(amount, cointype, null, 0)
@@ -288,22 +329,25 @@
return amount
-/obj/machinery/computer/slot_machine/proc/dispense(amount = 0, cointype = /obj/item/coin/silver, mob/living/target, throwit = FALSE)
- var/value = GLOB.coin_values[cointype] || GLOB.coin_values[/obj/item/coin/iron]
- INVOKE_ASYNC(src, .proc/become_rich, amount, value, cointype, target, throwit)
- return amount % value
+/obj/machinery/computer/slot_machine/proc/dispense(amount = 0, cointype = /obj/item/coin/silver, mob/living/target, throwit = 0)
+ if(paymode == HOLOCHIP)
+ var/obj/item/holochip/H = new /obj/item/holochip(loc,amount)
-/obj/machinery/computer/slot_machine/proc/become_rich(amount, value, cointype = /obj/item/coin/silver, mob/living/target, throwit = FALSE)
- if(value <= 0)
- return
- while(amount >= value && !QDELETED(src))
- var/obj/item/coin/C = new cointype(loc) //DOUBLE THE PAIN
- amount -= value
if(throwit && target)
- C.throw_at(target, 3, 10)
- else
- random_step(C, 2, 40)
- CHECK_TICK
+ H.throw_at(target, 3, 10)
+ else
+ var/value = coinvalues["[cointype]"]
+ if(value <= 0)
+ CRASH("Coin value of zero, refusing to payout in dispenser")
+ while(amount >= value)
+ var/obj/item/coin/C = new cointype(loc) //DOUBLE THE PAIN
+ amount -= value
+ if(throwit && target)
+ C.throw_at(target, 3, 10)
+ else
+ random_step(C, 2, 40)
+
+ return amount
#undef SEVEN
#undef SPIN_TIME
@@ -311,3 +355,5 @@
#undef BIG_PRIZE
#undef SMALL_PRIZE
#undef SPIN_PRICE
+#undef HOLOCHIP
+#undef COIN
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 88a5c3f20f..036aa6ed79 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -578,6 +578,7 @@
/obj/mecha/proc/mechstep(direction)
var/current_dir = dir
+ set_glide_size(DELAY_TO_GLIDE_SIZE(step_in))
var/result = step(src,direction)
if(strafe)
setDir(current_dir)
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 5142ba69ac..27b0aa4e66 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -87,16 +87,22 @@
M.IgniteMob()
/atom/movable/proc/unbuckle_mob(mob/living/buckled_mob, force=FALSE)
- if(istype(buckled_mob) && buckled_mob.buckled == src && (buckled_mob.can_unbuckle() || force))
- . = buckled_mob
- buckled_mob.buckled = null
- buckled_mob.anchored = initial(buckled_mob.anchored)
- buckled_mob.update_mobility()
- buckled_mob.clear_alert("buckled")
- buckled_mobs -= buckled_mob
- SEND_SIGNAL(src, COMSIG_MOVABLE_UNBUCKLE, buckled_mob, force)
+ if(!isliving(buckled_mob))
+ CRASH("Non-living [buckled_mob] thing called unbuckle_mob() for source.")
+ if(buckled_mob.buckled != src)
+ CRASH("[buckled_mob] called unbuckle_mob() for source while having buckled as [buckled_mob.buckled].")
+ if(!force && !buckled_mob.can_unbuckle())
+ return
+ . = buckled_mob
+ buckled_mob.buckled = null
+ buckled_mob.anchored = initial(buckled_mob.anchored)
+ buckled_mob.update_mobility()
+ buckled_mob.clear_alert("buckled")
+ buckled_mob.set_glide_size(DELAY_TO_GLIDE_SIZE(buckled_mob.total_multiplicative_slowdown()))
+ buckled_mobs -= buckled_mob
+ SEND_SIGNAL(src, COMSIG_MOVABLE_UNBUCKLE, buckled_mob, force)
- post_unbuckle_mob(.)
+ post_unbuckle_mob(.)
/atom/movable/proc/unbuckle_all_mobs(force=FALSE)
if(!has_buckled_mobs())
diff --git a/code/game/objects/effects/alien_acid.dm b/code/game/objects/effects/alien_acid.dm
index 5276062121..6496b392c3 100644
--- a/code/game/objects/effects/alien_acid.dm
+++ b/code/game/objects/effects/alien_acid.dm
@@ -53,6 +53,7 @@
return 0
/obj/effect/acid/Crossed(AM as mob|obj)
+ . = ..()
if(isliving(AM))
var/mob/living/L = AM
if(L.movement_type & FLYING)
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 9d6d034364..8f8b83bfde 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -138,7 +138,7 @@
/obj/effect/anomaly/grav/high/Initialize(mapload, new_lifespan)
. = ..()
- setup_grav_field()
+ INVOKE_ASYNC(src, .proc/setup_grav_field)
/obj/effect/anomaly/grav/high/proc/setup_grav_field()
grav_field = make_field(/datum/proximity_monitor/advanced/gravity, list("current_range" = 7, "host" = src, "gravity_value" = rand(0,3)))
diff --git a/code/game/objects/effects/decals/cleanable/robots.dm b/code/game/objects/effects/decals/cleanable/robots.dm
index 8f8f27c3c6..9dd3c2a852 100644
--- a/code/game/objects/effects/decals/cleanable/robots.dm
+++ b/code/game/objects/effects/decals/cleanable/robots.dm
@@ -69,4 +69,5 @@
persistent = FALSE
/obj/effect/decal/cleanable/oil/slippery/Initialize()
+ . = ..()
AddComponent(/datum/component/slippery, 80, (NO_SLIP_WHEN_WALKING | SLIDE))
diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm
index b310119827..6be0a50a5e 100644
--- a/code/game/objects/effects/overlays.dm
+++ b/code/game/objects/effects/overlays.dm
@@ -49,6 +49,42 @@
/obj/effect/overlay/vis
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
- vis_flags = NONE
- var/unused = 0 //When detected to be unused it gets set to world.time, after a while it gets removed
- var/cache_expiration = 2 MINUTES // overlays which go unused for 2 minutes get cleaned up
+ vis_flags = VIS_INHERIT_DIR
+ ///When detected to be unused it gets set to world.time, after a while it gets removed
+ var/unused = 0
+ ///overlays which go unused for this amount of time get cleaned up
+ var/cache_expiration = 2 MINUTES
+
+// /obj/effect/overlay/atmos_excited
+// name = "excited group"
+// icon = null
+// icon_state = null
+// anchored = TRUE // should only appear in vis_contents, but to be safe
+// appearance_flags = RESET_TRANSFORM | TILE_BOUND
+// invisibility = INVISIBILITY_ABSTRACT
+// mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+
+// layer = ATMOS_GROUP_LAYER
+// plane = ATMOS_GROUP_PLANE
+
+// /obj/effect/overlay/light_visible
+// name = ""
+// icon = 'icons/effects/light_overlays/light_32.dmi'
+// icon_state = "light"
+// layer = O_LIGHTING_VISUAL_LAYER
+// plane = O_LIGHTING_VISUAL_PLANE
+// appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
+// mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+// alpha = 0
+// vis_flags = NONE
+
+// /obj/effect/overlay/light_cone
+// name = ""
+// icon = 'icons/effects/light_overlays/light_cone.dmi'
+// icon_state = "light"
+// layer = O_LIGHTING_VISUAL_LAYER
+// plane = O_LIGHTING_VISUAL_PLANE
+// appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
+// mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+// vis_flags = NONE
+// alpha = 110
diff --git a/code/game/objects/effects/proximity.dm b/code/game/objects/effects/proximity.dm
index ab35d213ea..5c477501f4 100644
--- a/code/game/objects/effects/proximity.dm
+++ b/code/game/objects/effects/proximity.dm
@@ -5,6 +5,7 @@
var/list/checkers //list of /obj/effect/abstract/proximity_checkers
var/current_range
var/ignore_if_not_on_turf //don't check turfs in range if the host's loc isn't a turf
+ var/wire = FALSE
/datum/proximity_monitor/New(atom/_host, range, _ignore_if_not_on_turf = TRUE)
checkers = list()
@@ -35,6 +36,8 @@
return ..()
/datum/proximity_monitor/proc/HandleMove()
+ SIGNAL_HANDLER
+
var/atom/_host = host
var/atom/new_host_loc = _host.loc
if(last_host_loc != new_host_loc)
@@ -58,6 +61,8 @@
var/atom/_host = host
var/atom/loc_to_use = ignore_if_not_on_turf ? _host.loc : get_turf(_host)
+ if(wire && !isturf(loc_to_use)) //it makes assemblies attached on wires work
+ loc_to_use = get_turf(loc_to_use)
if(!isturf(loc_to_use)) //only check the host's loc
if(range)
var/obj/effect/abstract/proximity_checker/pc
@@ -109,4 +114,5 @@
/obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM)
set waitfor = FALSE
- monitor?.hasprox_receiver.HasProximity(AM)
+ . = ..()
+ monitor?.hasprox_receiver?.HasProximity(AM)
diff --git a/code/game/objects/effects/spawners/bundle.dm b/code/game/objects/effects/spawners/bundle.dm
index 4304fc3c9c..58cd0f4fa9 100644
--- a/code/game/objects/effects/spawners/bundle.dm
+++ b/code/game/objects/effects/spawners/bundle.dm
@@ -170,6 +170,7 @@
/obj/item/clothing/under/rank/civilian/mime/sexy)
/obj/effect/spawner/bundle/crate/Initialize(mapload)
+ SHOULD_CALL_PARENT(FALSE)
if(items && items.len)
var/turf/T = get_turf(src)
var/obj/structure/closet/LC = locate(/obj/structure/closet) in T
diff --git a/code/game/objects/effects/spawners/xeno_egg_delivery.dm b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
index 9be52dab52..dd4a6ea479 100644
--- a/code/game/objects/effects/spawners/xeno_egg_delivery.dm
+++ b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
@@ -15,5 +15,5 @@
message_admins("An alien egg has been delivered to [ADMIN_VERBOSEJMP(T)].")
log_game("An alien egg has been delivered to [AREACOORD(T)]")
var/message = "Attention [station_name()], we have entrusted you with a research specimen in [get_area_name(T, TRUE)]. Remember to follow all safety precautions when dealing with the specimen."
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/addtimer, CALLBACK(GLOBAL_PROC, /proc/print_command_report, message), announcement_time))
+ SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/_addtimer, CALLBACK(GLOBAL_PROC, /proc/print_command_report, message), announcement_time))
return INITIALIZE_HINT_QDEL
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 1c75f1e533..608768a0c7 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -11,7 +11,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
name = "item"
icon = 'icons/obj/items_and_weapons.dmi'
blocks_emissive = EMISSIVE_BLOCK_GENERIC
-
+
attack_hand_speed = 0
attack_hand_is_action = FALSE
attack_hand_unwieldlyness = 0
@@ -428,18 +428,19 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/proc/talk_into(mob/M, input, channel, spans, datum/language/language)
return ITALICS | REDUCE_RANGE
-/obj/item/proc/dropped(mob/user)
+/// Called when a mob drops an item.
+/obj/item/proc/dropped(mob/user, silent = FALSE)
SHOULD_CALL_PARENT(TRUE)
- current_equipped_slot = null
for(var/X in actions)
var/datum/action/A = X
A.Remove(user)
if(item_flags & DROPDEL)
qdel(src)
item_flags &= ~IN_INVENTORY
- if(SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user) & COMPONENT_DROPPED_RELOCATION)
- . = ITEM_RELOCATED_BY_DROPPED
- user.update_equipment_speed_mods()
+ SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
+ // if(!silent)
+ // playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
+ user?.update_equipment_speed_mods()
// called just as an item is picked up (loc is not yet changed)
/obj/item/proc/pickup(mob/user)
@@ -467,29 +468,43 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
melee_attack_chain(usr, over)
usr.FlushCurrentAction()
return TRUE //returning TRUE as a "is this overridden?" flag
+ if(isrevenant(usr))
+ if(RevenantThrow(over, usr, src))
+ return
+
if(!Adjacent(usr) || !over.Adjacent(usr))
return // should stop you from dragging through windows
over.MouseDrop_T(src,usr)
return
-// called after an item is placed in an equipment slot
-// user is mob that equipped it
-// slot uses the slot_X defines found in setup.dm
-// for items that can be placed in multiple slots
-// note this isn't called during the initial dressing of a player
-/obj/item/proc/equipped(mob/user, slot)
+/**
+ * Called after an item is placed in an equipment slot.
+ *
+ * Note that hands count as slots.
+ *
+ * Arguments:
+ * * user is mob that equipped it
+ * * slot uses the slot_X defines found in setup.dm for items that can be placed in multiple slots
+ * * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
+ */
+/obj/item/proc/equipped(mob/user, slot, initial = FALSE)
SHOULD_CALL_PARENT(TRUE)
- . = SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
+ SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
current_equipped_slot = slot
- if(!(. & COMPONENT_NO_GRANT_ACTIONS))
- for(var/X in actions)
- var/datum/action/A = X
- if(item_action_slot_check(slot, user, A)) //some items only give their actions buttons when in a specific slot.
- A.Grant(user)
+ for(var/X in actions)
+ var/datum/action/A = X
+ if(item_action_slot_check(slot, user, A)) //some items only give their actions buttons when in a specific slot.
+ A.Grant(user)
item_flags |= IN_INVENTORY
+ // if(!initial)
+ // if(equip_sound && (slot_flags & slot))
+ // playsound(src, equip_sound, EQUIP_SOUND_VOLUME, TRUE, ignore_walls = FALSE)
+ // else if(slot == ITEM_SLOT_HANDS)
+ // playsound(src, pickup_sound, PICKUP_SOUND_VOLUME, ignore_walls = FALSE)
user.update_equipment_speed_mods()
+
//Overlays for the worn overlay so you can overlay while you overlay
//eg: ammo counters, primed grenade flashing, etc.
//"icon_file" is used automatically for inhands etc. to make sure it gets the right inhand file
@@ -656,6 +671,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
else
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
+ // else
+ // playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
@@ -926,6 +943,10 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/proc/get_part_rating()
return 0
+//Can this item be given to people?
+/obj/item/proc/can_give()
+ return TRUE
+
/obj/item/doMove(atom/destination)
if (ismob(loc))
var/mob/M = loc
diff --git a/code/game/objects/items/RCL.dm b/code/game/objects/items/RCL.dm
index 6e305c30ee..e0ac4b43c6 100644
--- a/code/game/objects/items/RCL.dm
+++ b/code/game/objects/items/RCL.dm
@@ -166,7 +166,7 @@
last = C
break
-obj/item/rcl/proc/getMobhook(mob/to_hook)
+/obj/item/rcl/proc/getMobhook(mob/to_hook)
if(listeningTo == to_hook)
return
if(listeningTo)
diff --git a/code/game/objects/items/candle.dm b/code/game/objects/items/candle.dm
index a3366e714e..960e3ce499 100644
--- a/code/game/objects/items/candle.dm
+++ b/code/game/objects/items/candle.dm
@@ -82,4 +82,8 @@
/obj/item/candle/infinite/hugbox
heats_space = FALSE
+/obj/item/candle/DoRevenantThrowEffects(atom/target)
+ if(!infinite)
+ put_out_candle()
+
#undef CANDLE_LUMINOSITY
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 5b1b86b9e4..7d2b0ed832 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -779,16 +779,40 @@
/obj/item/card/id/departmental_budget/update_label()
return
+/obj/item/card/id/departmental_budget/civ
+ department_ID = ACCOUNT_CIV
+ department_name = ACCOUNT_CIV_NAME
+
+/obj/item/card/id/departmental_budget/eng
+ department_ID = ACCOUNT_ENG
+ department_name = ACCOUNT_ENG_NAME
+
+/obj/item/card/id/departmental_budget/sci
+ department_ID = ACCOUNT_SCI
+ department_name = ACCOUNT_SCI_NAME
+
+/obj/item/card/id/departmental_budget/med
+ department_ID = ACCOUNT_MED
+ department_name = ACCOUNT_MED_NAME
+
+/obj/item/card/id/departmental_budget/srv
+ department_ID = ACCOUNT_SRV
+ department_name = ACCOUNT_SRV_NAME
+
/obj/item/card/id/departmental_budget/car
department_ID = ACCOUNT_CAR
department_name = ACCOUNT_CAR_NAME
+/obj/item/card/id/departmental_budget/sec
+ department_ID = ACCOUNT_SEC
+ department_name = ACCOUNT_SEC_NAME
+
//Polychromatic Knight Badge
/obj/item/card/id/knight
name = "knight badge"
icon_state = "knight"
- desc = "A badge denoting the owner as a knight! It has a strip for swiping like an ID"
+ desc = "A badge denoting the owner as a knight! It has a strip for swiping like an ID."
var/id_color = "#00FF00" //defaults to green
var/mutable_appearance/id_overlay
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index de32375642..4280e7105f 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -136,6 +136,12 @@ CIGARETTE PACKETS ARE IN FANCY.DM
STOP_PROCESSING(SSobj, src)
. = ..()
+/obj/item/clothing/mask/cigarette/DoRevenantThrowEffects(atom/target)
+ if(lit)
+ attackby()
+ else
+ light()
+
/obj/item/clothing/mask/cigarette/attackby(obj/item/W, mob/user, params)
if(!lit && smoketime > 0)
var/lighting_text = W.ignition_effect(src, user)
@@ -517,6 +523,9 @@ CIGARETTE PACKETS ARE IN FANCY.DM
overlay_state = pick(overlay_list)
update_icon()
+/obj/item/lighter/DoRevenantThrowEffects(atom/target)
+ set_lit()
+
/obj/item/lighter/suicide_act(mob/living/carbon/user)
if (lit)
user.visible_message("[user] begins holding \the [src]'s flame up to [user.p_their()] face! It looks like [user.p_theyre()] trying to commit suicide!")
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index 4873962587..89d566f714 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -263,6 +263,7 @@
/obj/machinery/vending/cola = "Robust Softdrinks",
/obj/machinery/vending/cigarette = "ShadyCigs Deluxe",
/obj/machinery/vending/games = "\improper Good Clean Fun",
+ /obj/machinery/vending/kink = "KinkMate",
/obj/machinery/vending/autodrobe = "AutoDrobe",
/obj/machinery/vending/assist = "\improper Vendomat",
/obj/machinery/vending/engivend = "\improper Engi-Vend",
diff --git a/code/game/objects/items/devices/dogborg_sleeper.dm b/code/game/objects/items/devices/dogborg_sleeper.dm
index 1f91196b24..4d57d05be4 100644
--- a/code/game/objects/items/devices/dogborg_sleeper.dm
+++ b/code/game/objects/items/devices/dogborg_sleeper.dm
@@ -347,9 +347,9 @@
last_hearcheck = world.time
for(var/mob/H in hearing_mobs)
if(!istype(H.loc, /obj/item/dogborg/sleeper))
- H.playsound_local(source, null, 45, falloff = 0, S = pred_death)
+ H.playsound_local(source, null, 45, S = pred_death)
else if(H in contents)
- H.playsound_local(source, null, 65, falloff = 0, S = prey_death)
+ H.playsound_local(source, null, 65, S = prey_death)
for(var/belly in T.vore_organs)
var/obj/belly/B = belly
for(var/atom/movable/thing in B)
@@ -391,9 +391,9 @@
last_hearcheck = world.time
for(var/mob/H in hearing_mobs)
if(!istype(H.loc, /obj/item/dogborg/sleeper))
- H.playsound_local(source, null, 45, falloff = 0, S = pred_digest)
+ H.playsound_local(source, null, 45, S = pred_digest)
else if(H in contents)
- H.playsound_local(source, null, 65, falloff = 0, S = prey_digest)
+ H.playsound_local(source, null, 65, S = prey_digest)
update_gut(hound)
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index a2459bce9f..e29519406a 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -37,11 +37,14 @@
/obj/item/flashlight/attack_self(mob/user)
on = !on
update_brightness(user)
- playsound(user, on ? 'sound/weapons/magin.ogg' : 'sound/weapons/magout.ogg', 40, 1)
+ playsound(src, on ? 'sound/weapons/magin.ogg' : 'sound/weapons/magout.ogg', 40, TRUE)
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
- return 1
+ return TRUE
+
+/obj/item/flashlight/DoRevenantThrowEffects(atom/target)
+ attack_self()
/obj/item/flashlight/suicide_act(mob/living/carbon/human/user)
if (user.eye_blind)
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index bb46577702..a0b695e29f 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -8,10 +8,11 @@
slot_flags = ITEM_SLOT_BELT
obj_flags = UNIQUE_RENAME
var/gpstag = "COM0"
+ var/starton = TRUE
/obj/item/gps/Initialize()
. = ..()
- AddComponent(/datum/component/gps/item, gpstag)
+ AddComponent(/datum/component/gps/item, gpstag, starton)
/obj/item/gps/science
icon_state = "gps-s"
@@ -26,6 +27,9 @@
gpstag = "MINE0"
desc = "A positioning system helpful for rescuing trapped or injured miners, keeping one on you at all times while mining might just save your life."
+/obj/item/gps/mining/off
+ starton = FALSE
+
/obj/item/gps/cyborg
icon_state = "gps-b"
gpstag = "BORG0"
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 6ac2d310a1..939d77a1f0 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -283,14 +283,12 @@ GLOBAL_LIST_INIT(channel_tokens, list(
SSradio.remove_object(src, GLOB.radiochannels[ch_name])
secure_radio_connections[ch_name] = null
- var/turf/T = user.drop_location()
- if(T)
- if(keyslot)
- keyslot.forceMove(T)
- keyslot = null
- if(keyslot2)
- keyslot2.forceMove(T)
- keyslot2 = null
+ if(keyslot)
+ user.put_in_hands(keyslot)
+ keyslot = null
+ if(keyslot2)
+ user.put_in_hands(keyslot2)
+ keyslot2 = null
recalculateChannels()
to_chat(user, "You pop out the encryption keys in the headset.")
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index d9bfa4b00e..cfe0ba705d 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -306,18 +306,6 @@ GENETICS SCANNER
if(T.name == "fluffy tongue")
temp_message += " Subject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant."
- //HECK
- else if(istype(O, /obj/item/organ/genital/penis))
- var/obj/item/organ/genital/penis/P = O
- if(P.length>20)
- temp_message += " Subject has a sizeable gentleman's organ at [P.length] inches."
-
- else if(istype(O, /obj/item/organ/genital/breasts))
- var/obj/item/organ/genital/breasts/Br = O
- if(Br.cached_size>5)
- temp_message += " Subject has a sizeable bosom with a [Br.size] cup."
-
-
//GENERAL HANDLER
if(!damage_message)
diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm
index 0d5c9a22aa..39c8143ee9 100644
--- a/code/game/objects/items/eightball.dm
+++ b/code/game/objects/items/eightball.dm
@@ -46,6 +46,9 @@
if(.)
new /obj/item/toy/eightball/haunted(loc)
+/obj/item/toy/eightball/DoRevenantThrowEffects(atom/target)
+ MakeHaunted()
+
/obj/item/toy/eightball/attack_self(mob/user)
if(shaking)
return
diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm
index b1f51f608d..c1579dfe15 100644
--- a/code/game/objects/items/extinguisher.dm
+++ b/code/game/objects/items/extinguisher.dm
@@ -235,16 +235,23 @@
return
EmptyExtinguisher(user)
-/obj/item/extinguisher/proc/EmptyExtinguisher(var/mob/user)
- if(loc == user && reagents.total_volume)
+/obj/item/extinguisher/DoRevenantThrowEffects(atom/target)
+ EmptyExtinguisher()
+
+/obj/item/extinguisher/proc/EmptyExtinguisher(mob/user)
+ if(!reagents.total_volume)
+ return
+ if(loc == user || !user)
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("[user] empties out \the [src] onto the floor using the release valve.", "You quietly empty out \the [src] by using its release valve.")
+ if(user)
+ user.visible_message("[user] empties out \the [src] onto the floor using the release valve.", "You quietly empty out \the [src] by using its release valve.")
+ else
+ user.visible_message("The release valve of \the [src] suddenly opens and sprays it's contents on the floor!")
//firebot assembly
/obj/item/extinguisher/attackby(obj/O, mob/user, params)
diff --git a/code/game/objects/items/grenades/plastic.dm b/code/game/objects/items/grenades/plastic.dm
index 69f89d0150..ee259c4050 100644
--- a/code/game/objects/items/grenades/plastic.dm
+++ b/code/game/objects/items/grenades/plastic.dm
@@ -85,6 +85,7 @@
/obj/item/grenade/plastic/Crossed(atom/movable/AM)
if(nadeassembly)
nadeassembly.Crossed(AM)
+ . = ..()
/obj/item/grenade/plastic/on_found(mob/finder)
if(nadeassembly)
diff --git a/code/game/objects/items/pinpointer.dm b/code/game/objects/items/pinpointer.dm
index abd9cec950..e47b2bb0d8 100644
--- a/code/game/objects/items/pinpointer.dm
+++ b/code/game/objects/items/pinpointer.dm
@@ -19,6 +19,8 @@
var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination
var/ignore_suit_sensor_level = FALSE // Do we find people even if their suit sensors are turned off
var/alert = FALSE // TRUE to display things more seriously
+ /// resets target on toggle
+ var/resets_target = TRUE
/obj/item/pinpointer/Initialize()
. = ..()
@@ -27,17 +29,22 @@
/obj/item/pinpointer/Destroy()
STOP_PROCESSING(SSfastprocess, src)
GLOB.pinpointer_list -= src
- target = null
+ unset_target()
return ..()
+/obj/item/pinpointer/DoRevenantThrowEffects(atom/target)
+ attack_self()
+
/obj/item/pinpointer/attack_self(mob/living/user)
active = !active
- user.visible_message("[user] [active ? "" : "de"]activates [user.p_their()] pinpointer.", "You [active ? "" : "de"]activate your pinpointer.")
+ if(user)
+ user.visible_message("[user] [active ? "" : "de"]activates [user.p_their()] pinpointer.", "You [active ? "" : "de"]activate your pinpointer.")
playsound(src, 'sound/items/screwdriver2.ogg', 50, 1)
if(active)
START_PROCESSING(SSfastprocess, src)
else
- target = null
+ if(resets_target)
+ unset_target()
STOP_PROCESSING(SSfastprocess, src)
update_icon()
@@ -50,6 +57,18 @@
/obj/item/pinpointer/proc/scan_for_target()
return
+/obj/item/pinpointer/proc/set_target(atom/movable/newtarget)
+ if(target)
+ unset_target()
+ target = newtarget
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/unset_target)
+
+/obj/item/pinpointer/proc/unset_target()
+ if(!target)
+ return
+ UnregisterSignal(target, COMSIG_PARENT_QDELETING)
+ target = null
+
/obj/item/pinpointer/update_overlays()
. = ..()
if(!active)
@@ -101,7 +120,8 @@
active = FALSE
user.visible_message("[user] deactivates [user.p_their()] pinpointer.", "You deactivate your pinpointer.")
playsound(src, 'sound/items/screwdriver2.ogg', 50, 1)
- target = null //Restarting the pinpointer forces a target reset
+ if(resets_target)
+ unset_target() //Restarting the pinpointer forces a target reset
STOP_PROCESSING(SSfastprocess, src)
update_icon()
return
@@ -137,7 +157,7 @@
if(!A || QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated())
return
- target = names[A]
+ set_target(names[A])
active = TRUE
user.visible_message("[user] activates [user.p_their()] pinpointer.", "You activate your pinpointer.")
playsound(src, 'sound/items/screwdriver2.ogg', 50, 1)
@@ -149,7 +169,7 @@
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(!trackable(H))
- target = null
+ unset_target()
if(!target) //target can be set to null from above code, or elsewhere
active = FALSE
@@ -163,7 +183,7 @@
. = ..()
/obj/item/pinpointer/pair/scan_for_target()
- target = other_pair
+ set_target(other_pair)
/obj/item/pinpointer/pair/examine(mob/user)
. = ..()
@@ -195,7 +215,7 @@
shuttleport = SSshuttle.getShuttle("huntership")
/obj/item/pinpointer/shuttle/scan_for_target()
- target = shuttleport
+ set_target(shuttleport)
/obj/item/pinpointer/shuttle/Destroy()
shuttleport = null
@@ -207,4 +227,8 @@
icon_state = "pinpointer_ian"
/obj/item/pinpointer/ian/scan_for_target()
- target = locate(/mob/living/simple_animal/pet/dog/corgi/Ian) in GLOB.mob_living_list
+ set_target(locate(/mob/living/simple_animal/pet/dog/corgi/Ian) in GLOB.mob_living_list)
+
+/obj/item/pinpointer/custom
+ resets_target = FALSE
+
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index d1174cded7..c213fa34df 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -45,6 +45,10 @@
return
set_snowflake_from_config(id)
+/obj/item/toy/plush/DoRevenantThrowEffects(atom/target)
+ var/datum/component/squeak/squeaker = GetComponent(/datum/component/squeak)
+ squeaker.do_play_squeak(TRUE)
+
/obj/item/toy/plush/Initialize(mapload, set_snowflake_id)
. = ..()
AddComponent(/datum/component/squeak, squeak_override)
@@ -462,6 +466,7 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
can_random_spawn = FALSE
/obj/item/toy/plush/random/Initialize()
+ SHOULD_CALL_PARENT(FALSE)
var/newtype
var/list/snowflake_list = CONFIG_GET(keyed_list/snowflake_plushies)
diff --git a/code/game/objects/items/pneumaticCannon.dm b/code/game/objects/items/pneumaticCannon.dm
index 23be8cbb9a..1db5cdd526 100644
--- a/code/game/objects/items/pneumaticCannon.dm
+++ b/code/game/objects/items/pneumaticCannon.dm
@@ -43,6 +43,13 @@
/obj/item/pneumatic_cannon/proc/init_charge() //wrapper so it can be vv'd easier
START_PROCESSING(SSobj, src)
+/obj/item/pneumatic_cannon/DoRevenantThrowEffects(atom/target)
+ var/picked_target
+ var/list/possible_targets = range(3,src)
+ picked_target = pick(possible_targets)
+ if(target)
+ Fire(null, picked_target)
+
/obj/item/pneumatic_cannon/process()
if(++charge_tick >= charge_ticks && charge_type)
fill_with_type(charge_type, charge_amount)
@@ -134,21 +141,29 @@
Fire(user, target)
/obj/item/pneumatic_cannon/proc/Fire(mob/living/user, var/atom/target)
- if(!istype(user) && !target)
+ if(!target)
return
+ if(user)
+ if(!isliving(user))
+ return
var/discharge = 0
- if(!can_trigger_gun(user))
+ if(user && !can_trigger_gun(user))
return
if(!loadedItems || !loadedWeightClass)
- to_chat(user, "\The [src] has nothing loaded.")
+ if(user)
+ to_chat(user, "\The [src] has nothing loaded.")
return
if(!tank && checktank)
- to_chat(user, "\The [src] can't fire without a source of gas.")
+ if(user)
+ to_chat(user, "\The [src] can't fire without a source of gas.")
return
if(tank && !tank.air_contents.remove(gasPerThrow * pressureSetting))
- to_chat(user, "\The [src] lets out a weak hiss and doesn't react!")
+ if(user)
+ to_chat(user, "\The [src] lets out a weak hiss and doesn't react!")
+ else
+ visible_message(src, "\The [src] lets out a weak hiss and doesn't react!")
return
- if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(75) && clumsyCheck && iscarbon(user))
+ if(user && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(75) && clumsyCheck && iscarbon(user))
var/mob/living/carbon/C = user
C.visible_message("[C] loses [C.p_their()] grip on [src], causing it to go off!", "[src] slips out of your hands and goes off!")
C.dropItemToGround(src, TRUE)
@@ -157,15 +172,18 @@
else
var/list/possible_targets = range(3,src)
target = pick(possible_targets)
- discharge = 1
- if(!discharge)
+ discharge = TRUE
+ if(!discharge && user)
user.visible_message("[user] fires \the [src]!", \
"You fire \the [src]!")
- log_combat(user, target, "fired at", src)
var/turf/T = get_target(target, get_turf(src))
- playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, 1)
- fire_items(T, user)
- if(pressureSetting >= 3 && iscarbon(user))
+ playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, TRUE)
+ if(user)
+ log_combat(user, target, "fired at", src)
+ fire_items(T, user)
+ else
+ fire_items(T)
+ if(user && pressureSetting >= 3 && iscarbon(user))
var/mob/living/carbon/C = user
C.visible_message("[C] is thrown down by the force of the cannon!", "[src] slams into your shoulder, knocking you down!")
C.DefaultCombatKnockdown(60)
diff --git a/code/game/objects/items/puzzle_pieces.dm b/code/game/objects/items/puzzle_pieces.dm
index d0fd4dc3f6..345f3185da 100644
--- a/code/game/objects/items/puzzle_pieces.dm
+++ b/code/game/objects/items/puzzle_pieces.dm
@@ -134,7 +134,7 @@
/obj/item/pressure_plate/hologrid/Crossed(atom/movable/AM)
. = ..()
if(trigger_item && istype(AM, specific_item) && !claimed)
- AM.anchored = TRUE
+ AM.set_anchored(TRUE)
flick("laserbox_burn", AM)
trigger()
QDEL_IN(src, 15)
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index d1d57fe375..5c3e4fd612 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -258,7 +258,7 @@
shield_flags = SHIELD_FLAGS_DEFAULT
max_integrity = 300
-obj/item/shield/riot/bullet_proof
+/obj/item/shield/riot/bullet_proof
name = "bullet resistant shield"
desc = "A far more frail shield made of resistant plastics and kevlar meant to block ballistics."
armor = list("melee" = 30, "bullet" = 80, "laser" = 0, "energy" = 0, "bomb" = -40, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50)
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 3e2bb675fa..520a400630 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -65,7 +65,7 @@
if(merge)
for(var/obj/item/stack/S in loc)
if(S.merge_type == merge_type)
- merge(S)
+ INVOKE_ASYNC(src, .proc/merge, S)
var/list/temp_recipes = get_main_recipes()
recipes = temp_recipes.Copy()
if(material_type)
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index f15588a7f1..f10167890a 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -639,7 +639,7 @@
new /obj/item/bikehorn(src)
new /obj/item/implanter/sad_trombone(src)
-obj/item/storage/backpack/duffelbag/syndie/shredderbundle
+/obj/item/storage/backpack/duffelbag/syndie/shredderbundle
desc = "A large duffel bag containing two CX Shredders, some magazines, an elite hardsuit, and a chest rig."
/obj/item/storage/backpack/duffelbag/syndie/shredderbundle/PopulateContents()
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 80818b5fff..9c3d776b56 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -857,7 +857,7 @@
icon_state = "2sheath"
item_state = "katana" //this'll do.
w_class = WEIGHT_CLASS_BULKY
- fitting_swords = list(/obj/item/melee/smith/wakizashi, /obj/item/melee/smith/twohand/katana, /obj/item/melee/bokken)
+ fitting_swords = list(/obj/item/melee/smith/wakizashi, /obj/item/melee/smith/twohand/katana, /obj/item/melee/bokken, /obj/item/katana)
starting_sword = null
/obj/item/storage/belt/sabre/twin/ComponentInitialize()
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index 975621ead1..3f9c3a019e 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -275,7 +275,7 @@
for(var/i in 1 to 7)
new /obj/item/grenade/flashbang(src)
-obj/item/storage/box/stingbangs
+/obj/item/storage/box/stingbangs
name = "box of stingbangs (WARNING)"
desc = "WARNING: These devices are extremely dangerous and can cause severe injuries or death in repeated use."
icon_state = "secbox"
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index 26051e65fd..1de77bd8b9 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -47,6 +47,9 @@
cell = new preload_cell_type(src)
update_icon()
+/obj/item/melee/baton/DoRevenantThrowEffects(atom/target)
+ switch_status()
+
/obj/item/melee/baton/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
//Only mob/living types have stun handling
@@ -236,6 +239,12 @@
if(!iscyborg(loc))
deductcharge(severity*10, TRUE, FALSE)
+/obj/item/melee/baton/can_give()
+ if(turned_on)
+ return FALSE
+ else
+ ..()
+
/obj/item/melee/baton/stunsword
name = "stunsword"
desc = "Not actually sharp, this sword is functionally identical to its baton counterpart."
@@ -269,21 +278,22 @@
icon_state = "refill_donksoft"
var/product = /obj/item/melee/baton/stunsword //what it makes
var/list/fromitem = list(/obj/item/melee/baton, /obj/item/melee/baton/loaded) //what it needs
- afterattack(obj/O, mob/user as mob)
- if(istype(O, product))
- to_chat(user,"[O] is already modified!")
- else if(O.type in fromitem) //makes sure O is the right thing
- var/obj/item/melee/baton/B = O
- if(!B.cell) //checks for a powercell in the baton. If there isn't one, continue. If there is, warn the user to take it out
- new product(usr.loc) //spawns the product
- user.visible_message("[user] modifies [O]!","You modify the [O]!")
- qdel(O) //Gets rid of the baton
- qdel(src) //gets rid of the kit
- else
- to_chat(user,"Remove the powercell first!") //We make this check because the stunsword starts without a battery.
+/obj/item/ssword_kit/afterattack(obj/O, mob/user as mob)
+ if(istype(O, product))
+ to_chat(user,"[O] is already modified!")
+ return
+ if(O.type in fromitem) //makes sure O is the right thing
+ var/obj/item/melee/baton/B = O
+ if(!B.cell) //checks for a powercell in the baton. If there isn't one, continue. If there is, warn the user to take it out
+ new product(usr.loc) //spawns the product
+ user.visible_message("[user] modifies [O]!","You modify the [O]!")
+ qdel(O) //Gets rid of the baton
+ qdel(src) //gets rid of the kit
else
- to_chat(user, " You can't modify [O] with this kit!")
+ to_chat(user,"Remove the powercell first!") //We make this check because the stunsword starts without a battery.
+ else
+ to_chat(user, " You can't modify [O] with this kit!")
//Makeshift stun baton. Replacement for stun gloves.
/obj/item/melee/baton/cattleprod
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index 08b91332ef..d57f0cc51f 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -73,6 +73,15 @@
/obj/item/tank/proc/populate_gas()
return
+/obj/item/tank/DoRevenantThrowEffects(atom/target)
+ if(air_contents)
+ var/turf/open/location = get_turf(src)
+ if(istype(location))
+ location.assume_air(air_contents)
+ air_contents.clear()
+ SSair.add_to_active(location)
+ visible_message("[src] can't be turned on while unsecured!")
+ if(user)
+ to_chat(user, "[src] can't be turned on while unsecured!")
return
welding = !welding
if(welding)
if(get_fuel() >= 1)
- to_chat(user, "You switch [src] on.")
+ if(user)
+ to_chat(user, "You switch [src] on.")
playsound(loc, acti_sound, 50, 1)
force = 15
damtype = "fire"
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 4e82df2452..97fced4eb7 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -531,6 +531,7 @@
pop_burst()
/obj/item/toy/snappop/Crossed(H as mob|obj)
+ . = ..()
if(ishuman(H) || issilicon(H)) //i guess carp and shit shouldn't set them off
var/mob/living/carbon/M = H
if(issilicon(H) || M.m_intent == MOVE_INTENT_RUN)
diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm
index a07e9a26d5..ff8287d77b 100644
--- a/code/game/objects/structures/plasticflaps.dm
+++ b/code/game/objects/structures/plasticflaps.dm
@@ -62,7 +62,7 @@
return 1
var/mob/living/M = caller
- if(!M.ventcrawler && M.mob_size != MOB_SIZE_TINY)
+ if(!(SEND_SIGNAL(M, COMSIG_CHECK_VENTCRAWL)) && M.mob_size != MOB_SIZE_TINY)
return 0
var/atom/movable/M = caller
if(M && M.pulling)
@@ -91,7 +91,7 @@
return 1
if(M.buckled && istype(M.buckled, /mob/living/simple_animal/bot/mulebot)) // mulebot passenger gets a free pass.
return 1
- if(!M.lying && !M.ventcrawler && M.mob_size != MOB_SIZE_TINY) //If your not laying down, or a ventcrawler or a small creature, no pass.
+ if(!M.lying && !(SEND_SIGNAL(M, COMSIG_CHECK_VENTCRAWL)) && M.mob_size != MOB_SIZE_TINY) //If your not laying down, or a ventcrawler or a small creature, no pass.
return 0
return ..()
diff --git a/code/game/objects/structures/signs/signs_maps.dm b/code/game/objects/structures/signs/signs_maps.dm
index ee29ef7443..bcd7c02c66 100644
--- a/code/game/objects/structures/signs/signs_maps.dm
+++ b/code/game/objects/structures/signs/signs_maps.dm
@@ -56,17 +56,17 @@
desc = "A direction sign, pointing out which way the Cafe is."
icon_state = "direction_cafe"
-obj/structure/sign/directions/rooms
+/obj/structure/sign/directions/rooms
name = "room"
desc = "Room numbers, helps others find you!"
icon_state = "roomnum"
-obj/structure/sign/directions/dorms
+/obj/structure/sign/directions/dorms
name = "dorm"
desc = "Dorm numbers, help others find you, or you find others."
icon_state = "dormnum"
-obj/structure/sign/directions/cells
+/obj/structure/sign/directions/cells
name = "room"
desc = "So the less fortunate amongst us know where they'll be staying."
icon_state = "cellnum"
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 31f11a199c..1ef0726e63 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -269,13 +269,13 @@
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
/obj/structure/table/rolling/Moved(atom/OldLoc, Dir)
+ . = ..()
for(var/mob/M in OldLoc.contents)//Kidnap everyone on top
M.forceMove(loc)
for(var/x in attached_items)
var/atom/movable/AM = x
if(!AM.Move(loc))
RemoveItemFromTable(AM, AM.loc)
- return TRUE
/*
* Glass tables
diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
index cf68341b2c..21709edf73 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
@@ -126,12 +126,13 @@
if(current_tube == null)
setDir(next_dir)
- Move(get_step(loc, dir), dir) // Allow collisions when leaving the tubes.
+ Move(get_step(loc, dir), dir, DELAY_TO_GLIDE_SIZE(exit_delay)) // Allow collisions when leaving the tubes.
break
last_delay = current_tube.enter_delay(src, next_dir)
sleep(last_delay)
setDir(next_dir)
+ set_glide_size(DELAY_TO_GLIDE_SIZE(last_delay + exit_delay))
forceMove(next_loc) // When moving from one tube to another, skip collision and such.
density = current_tube.density
diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm
index 4c8379d14f..e6a76f36ca 100644
--- a/code/game/objects/structures/traps.dm
+++ b/code/game/objects/structures/traps.dm
@@ -56,6 +56,7 @@
animate(src, alpha = initial(alpha), time = time_between_triggers)
/obj/structure/trap/Crossed(atom/movable/AM)
+ . = ..()
if(last_trigger + time_between_triggers > world.time)
return
// Don't want the traps triggered by sparks, ghosts or projectiles.
@@ -67,6 +68,7 @@
return
if(M.anti_magic_check())
flare()
+ return
if(charges <= 0)
return
flare()
diff --git a/code/game/say.dm b/code/game/say.dm
index 3bc14ed245..b7bfe540d0 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -59,7 +59,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
var/endspanpart = ""
//Message
- var/messagepart = " "
+ var/messagepart = " "
var/languageicon = ""
var/datum/language/D = GLOB.language_datum_instances[message_language]
@@ -116,6 +116,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode, no_quote = FALSE)
if(has_language(language))
var/atom/movable/AM = speaker.GetSource()
+ raw_message = say_emphasis(raw_message)
if(AM) //Basically means "if the speaker is virtual"
return no_quote ? AM.quoteless_say_quote(raw_message, spans, message_mode) : AM.say_quote(raw_message, spans, message_mode)
else
diff --git a/code/game/sound.dm b/code/game/sound.dm
index b012ba4e2a..1816336bd1 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -1,4 +1,49 @@
-/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, soundenvwet = -10000, soundenvdry = 0)
+// /tg/ reverb disabled
+/*
+///Default override for echo
+/sound
+ echo = list(
+ 0, // Direct
+ 0, // DirectHF
+ -10000, // Room, -10000 means no low frequency sound reverb
+ -10000, // RoomHF, -10000 means no high frequency sound reverb
+ 0, // Obstruction
+ 0, // ObstructionLFRatio
+ 0, // Occlusion
+ 0.25, // OcclusionLFRatio
+ 1.5, // OcclusionRoomRatio
+ 1.0, // OcclusionDirectRatio
+ 0, // Exclusion
+ 1.0, // ExclusionLFRatio
+ 0, // OutsideVolumeHF
+ 0, // DopplerFactor
+ 0, // RolloffFactor
+ 0, // RoomRolloffFactor
+ 1.0, // AirAbsorptionFactor
+ 0, // Flags (1 = Auto Direct, 2 = Auto Room, 4 = Auto RoomHF)
+ )
+ environment = SOUND_ENVIRONMENT_NONE //Default to none so sounds without overrides dont get reverb
+*/
+
+/*! playsound
+
+playsound is a proc used to play a 3D sound in a specific range. This uses SOUND_RANGE + extra_range to determine that.
+
+source - Origin of sound
+soundin - Either a file, or a string that can be used to get an SFX
+vol - The volume of the sound, excluding falloff and pressure affection.
+vary - bool that determines if the sound changes pitch every time it plays
+extrarange - modifier for sound range. This gets added on top of SOUND_RANGE
+falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive.
+frequency - playback speed of audio
+channel - The channel the sound is played at
+pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space)
+ignore_walls - Whether or not the sound can pass through walls.
+falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range.
+
+*/
+
+/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, envwet = -10000, envdry = 0)
if(isarea(source))
CRASH("playsound(): source is an area")
@@ -10,26 +55,60 @@
//allocate a channel if necessary now so its the same for everyone
channel = channel || SSsounds.random_available_channel()
- // Looping through the player list has the added bonus of working for mobs inside containers
+ // Looping through the player list has the added bonus of working for mobs inside containers
var/sound/S = sound(get_sfx(soundin))
- var/maxdistance = (world.view + extrarange)
- var/z = turf_source.z
- var/list/listeners = SSmobs.clients_by_zlevel[z]
+ var/maxdistance = SOUND_RANGE + extrarange
+ var/source_z = turf_source.z
+ var/list/listeners = SSmobs.clients_by_zlevel[source_z].Copy()
+
+ var/turf/above_turf = SSmapping.get_turf_above(turf_source)
+ var/turf/below_turf = SSmapping.get_turf_below(turf_source)
+
if(!ignore_walls) //these sounds don't carry through walls
listeners = listeners & hearers(maxdistance,turf_source)
+
+ if(above_turf && istransparentturf(above_turf))
+ listeners += hearers(maxdistance,above_turf)
+
+ if(below_turf && istransparentturf(turf_source))
+ listeners += hearers(maxdistance,below_turf)
+
+ else
+ if(above_turf && istransparentturf(above_turf))
+ listeners += SSmobs.clients_by_zlevel[above_turf.z]
+
+ if(below_turf && istransparentturf(turf_source))
+ listeners += SSmobs.clients_by_zlevel[below_turf.z]
+
for(var/P in listeners)
var/mob/M = P
if(get_dist(M, turf_source) <= maxdistance)
- M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, soundenvwet, soundenvdry)
- for(var/P in SSmobs.dead_players_by_zlevel[z])
+ M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, null, envwet, envdry)
+ for(var/P in SSmobs.dead_players_by_zlevel[source_z])
var/mob/M = P
if(get_dist(M, turf_source) <= maxdistance)
- M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, soundenvwet, soundenvdry)
+ M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, null, envwet, envdry)
-/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, envwet = -10000, envdry = 0, manual_x, manual_y, distance_multiplier = 1)
- if(audiovisual_redirect)
- var/turf/T = get_turf(src)
- audiovisual_redirect.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, 0, -1000, turf_source.x - T.x, turf_source.y - T.y, distance_multiplier)
+/*! playsound
+
+playsound_local is a proc used to play a sound directly on a mob from a specific turf.
+This is called by playsound to send sounds to players, in which case it also gets the max_distance of that sound.
+
+turf_source - Origin of sound
+soundin - Either a file, or a string that can be used to get an SFX
+vol - The volume of the sound, excluding falloff
+vary - bool that determines if the sound changes pitch every time it plays
+frequency - playback speed of audio
+falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive.
+channel - The channel the sound is played at
+pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space)
+max_distance - The peak distance of the sound, if this is a 3D sound
+falloff_distance - Distance at which falloff begins, if this is a 3D sound
+distance_multiplier - Can be used to multiply the distance at which the sound is heard
+
+*/
+
+/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, envwet = -10000, envdry = 0)
if(!client || !can_hear())
return
@@ -39,7 +118,9 @@
S.wait = 0 //No queue
S.channel = channel || SSsounds.random_available_channel()
S.volume = vol
+ // CITADEL EDIT - Force citadel reverb
S.environment = 7
+ // End
if(vary)
if(frequency)
@@ -51,13 +132,13 @@
var/turf/T = get_turf(src)
//sound volume falloff with distance
- var/distance = 0
- if(!manual_x && !manual_y)
- distance = get_dist(T, turf_source)
+ var/distance = get_dist(T, turf_source)
distance *= distance_multiplier
- S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff.
+ if(max_distance) //If theres no max_distance we're not a 3D sound, so no falloff.
+ S.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * S.volume
+ //https://www.desmos.com/calculator/sqdfl8ipgf
if(pressure_affected)
//Atmosphere affects sound
@@ -72,42 +153,57 @@
else //space
pressure_factor = 0
- S.echo = list(envdry, null, envwet, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, null, null)
-
if(distance <= 1)
pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound
S.volume *= pressure_factor
//End Atmosphere affecting sound
+ /// Citadel edit - Citadel reverb
+ S.echo = list(envdry, null, envwet, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, null, null)
+ /// End
+
if(S.volume <= 0)
return //No sound
- var/dx = 0 // Hearing from the right/left
- if(!manual_x)
- dx = turf_source.x - T.x
- else
- dx = manual_x
+ var/dx = turf_source.x - T.x // Hearing from the right/left
S.x = dx * distance_multiplier
- var/dz = 0 // Hearing from infront/behind
- if(!manual_x)
- dz = turf_source.y - T.y
- else
- dz = manual_y
+ var/dz = turf_source.y - T.y // Hearing from infront/behind
S.z = dz * distance_multiplier
- // The y value is for above your head, but there is no ceiling in 2d spessmens.
- S.y = 1
- S.falloff = (falloff ? falloff : FALLOFF_SOUNDS)
+ var/dy = (turf_source.z - T.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords.
+ S.y = dy
+
+ S.falloff = isnull(max_distance)? FALLOFF_SOUNDS : max_distance //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant.
+
+ /*
+ /// Tg reverb removed
+ if(S.environment == SOUND_ENVIRONMENT_NONE)
+ if(sound_environment_override != SOUND_ENVIRONMENT_NONE)
+ S.environment = sound_environment_override
+ else
+ var/area/A = get_area(src)
+ if(A.sound_environment != SOUND_ENVIRONMENT_NONE)
+ S.environment = A.sound_environment
+
+ if(use_reverb)
+ if(S.environment == SOUND_ENVIRONMENT_NONE) //We have reverb, reset our echo setting
+ S.environment = SOUND_ENVIRONMENT_CONCERT_HALL
+ S.echo = list(0, null, -10000, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, null, null)
+ else
+ S.echo[3] = 0 //Room setting, 0 means normal reverb
+ S.echo[4] = 0 //RoomHF setting, 0 means normal reverb.
+ */
+ ///
SEND_SOUND(src, S)
-/proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, falloff = FALSE, channel = 0, pressure_affected = FALSE, sound/S)
+/proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, channel = 0, pressure_affected = FALSE, sound/S)
if(!S)
S = sound(get_sfx(soundin))
for(var/m in GLOB.player_list)
if(ismob(m) && !isnewplayer(m))
var/mob/M = m
- M.playsound_local(M, null, volume, vary, frequency, falloff, channel, pressure_affected, S)
+ M.playsound_local(M, null, volume, vary, frequency, null, channel, pressure_affected, S)
/mob/proc/stop_sound_channel(chan)
SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = chan))
@@ -130,14 +226,14 @@
/proc/get_sfx(soundin)
if(istext(soundin))
switch(soundin)
- if ("explosion_creaking") // from skyrat-ss13/skyrat13/pull/3295
- soundin = pick('sound/effects/explosioncreak1.ogg', 'sound/effects/explosioncreak2.ogg')
- if ("hull_creaking") // from skyrat-ss13/skyrat13/pull/3295
- soundin = pick('sound/effects/creak1.ogg', 'sound/effects/creak2.ogg', 'sound/effects/creak3.ogg')
if ("shatter")
soundin = pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg')
if ("explosion")
soundin = pick('sound/effects/explosion1.ogg','sound/effects/explosion2.ogg')
+ if ("explosion_creaking")
+ soundin = pick('sound/effects/explosioncreak1.ogg', 'sound/effects/explosioncreak2.ogg')
+ if ("hull_creaking")
+ soundin = pick('sound/effects/creak1.ogg', 'sound/effects/creak2.ogg', 'sound/effects/creak3.ogg')
if ("sparks")
soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks4.ogg')
if ("rustle")
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 7e00b1cf63..876c170923 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -1,12 +1,5 @@
/turf/open
plane = FLOOR_PLANE
- /// Does dirt buildup happen on us?
- var/dirt_buildup_allowed = FALSE
- /// Dirt level.
- var/dirtyness = 0
- /// Dirt level to spawn dirt. Null to use config.
- var/dirt_spawn_threshold
- /// Slowdown applied to mobs on us.
var/slowdown = 0 //negative for faster, positive for slower
var/postdig_icon_change = FALSE
@@ -18,6 +11,15 @@
var/clawfootstep = null
var/heavyfootstep = null
+ /// Dirtyness system, cit specific.
+
+ /// Does dirt buildup happen on us?
+ var/dirt_buildup_allowed = FALSE
+ /// Dirt level.
+ var/dirtyness = 0
+ /// Dirt level to spawn dirt. Null to use config.
+ var/dirt_spawn_threshold
+
/turf/open/ComponentInitialize()
. = ..()
if(wet)
diff --git a/code/game/turfs/openspace/openspace.dm b/code/game/turfs/openspace/openspace.dm
index 0607f19dd3..69bdcf06cf 100644
--- a/code/game/turfs/openspace/openspace.dm
+++ b/code/game/turfs/openspace/openspace.dm
@@ -10,6 +10,8 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr
plane = OPENSPACE_BACKDROP_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = SPLASHSCREEN_LAYER
+ //I don't know why the others are aligned but I shall do the same.
+ vis_flags = VIS_INHERIT_ID
/turf/open/transparent/openspace
name = "open space"
@@ -17,6 +19,7 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr
icon_state = "transparent"
baseturfs = /turf/open/transparent/openspace
CanAtmosPassVertical = ATMOS_PASS_YES
+ intact = FALSE //this means wires go on top
//mouse_opacity = MOUSE_OPACITY_TRANSPARENT
var/can_cover_up = TRUE
var/can_build_on = TRUE
@@ -32,10 +35,14 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr
/turf/open/transparent/openspace/show_bottom_level()
return FALSE
-/turf/open/transparent/openspace/Initialize() // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker
+/turf/open/openspace/Initialize() // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker
. = ..()
-
vis_contents += GLOB.openspace_backdrop_one_for_all //Special grey square for projecting backdrop darkness filter on it.
+ return INITIALIZE_HINT_LATELOAD
+
+/turf/open/openspace/LateInitialize()
+ . = ..()
+ // AddElement(/datum/element/turf_z_transparency, FALSE)
/turf/open/transparent/openspace/can_have_cabling()
if(locate(/obj/structure/lattice/catwalk, src))
@@ -95,6 +102,7 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr
return
if(L)
if(R.use(1))
+ qdel(L)
to_chat(user, "You construct a catwalk.")
playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
new/obj/structure/lattice/catwalk(src)
@@ -148,9 +156,22 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr
/turf/open/transparent/openspace/icemoon
name = "ice chasm"
baseturfs = /turf/open/transparent/openspace/icemoon
- can_cover_up = TRUE
- can_build_on = TRUE
initial_gas_mix = ICEMOON_DEFAULT_ATMOS
+ planetary_atmos = TRUE
+ var/replacement_turf = /turf/open/floor/plating/asteroid/snow/icemoon
+
+/turf/open/transparent/openspace/icemoon/Initialize()
+ . = ..()
+ var/turf/T = below()
+ // if(T.flags_1 & NO_RUINS_1)
+ // ChangeTurf(replacement_turf, null, CHANGETURF_IGNORE_AIR)
+ // return
+ // if(!ismineralturf(T))
+ // return
+ var/turf/closed/mineral/M = T
+ M.mineralAmt = 0
+ M.gets_drilled()
+ baseturfs = /turf/open/transparent/openspace/icemoon //This is to ensure that IF random turf generation produces a openturf, there won't be other turfs assigned other than openspace.
/turf/open/transparent/openspace/icemoon/can_zFall(atom/movable/A, levels = 1, turf/target)
return TRUE
diff --git a/code/game/turfs/openspace/transparent.dm b/code/game/turfs/openspace/transparent.dm
index 8efc60a15b..fa5bc17638 100644
--- a/code/game/turfs/openspace/transparent.dm
+++ b/code/game/turfs/openspace/transparent.dm
@@ -66,7 +66,12 @@
/turf/open/transparent/glass/Initialize()
icon_state = "" //Prevent the normal icon from appearing behind the smooth overlays
- return ..()
+ ..()
+ return INITIALIZE_HINT_LATELOAD
+
+/turf/open/floor/glass/LateInitialize()
+ . = ..()
+ // AddElement(/datum/element/turf_z_transparency, TRUE)
/turf/open/transparent/glass/wrench_act(mob/living/user, obj/item/I)
to_chat(user, "You begin removing glass...")
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index 9c9639e57c..0905fb2e9c 100644
--- a/code/game/turfs/space/space.dm
+++ b/code/game/turfs/space/space.dm
@@ -20,12 +20,19 @@
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
bullet_bounce_sound = null
+ vis_flags = VIS_INHERIT_ID //when this be added to vis_contents of something it be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf.
/turf/open/space/basic/New() //Do not convert to Initialize
//This is used to optimize the map loader
return
+/**
+ * Space Initialize
+ *
+ * Doesn't call parent, see [/atom/proc/Initialize]
+ */
/turf/open/space/Initialize()
+ SHOULD_CALL_PARENT(FALSE)
icon_state = SPACE_ICON_STATE
air = space_gas
update_air_ref()
@@ -36,6 +43,15 @@
stack_trace("Warning: [src]([type]) initialized multiple times!")
flags_1 |= INITIALIZED_1
+ // if (length(smoothing_groups))
+ // sortTim(smoothing_groups) //In case it's not properly ordered, let's avoid duplicate entries with the same values.
+ // SET_BITFLAG_LIST(smoothing_groups)
+ // if (length(canSmoothWith))
+ // sortTim(canSmoothWith)
+ // if(canSmoothWith[length(canSmoothWith)] > MAX_S_TURF) //If the last element is higher than the maximum turf-only value, then it must scan turf contents for smoothing targets.
+ // smoothing_flags |= SMOOTH_OBJ
+ // SET_BITFLAG_LIST(canSmoothWith)
+
var/area/A = loc
if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A))
add_overlay(/obj/effect/fullbright)
@@ -49,6 +65,13 @@
if (opacity)
has_opaque_atom = TRUE
+ var/turf/T = SSmapping.get_turf_above(src)
+ if(T)
+ T.multiz_turf_new(src, DOWN)
+ T = SSmapping.get_turf_below(src)
+ if(T)
+ T.multiz_turf_new(src, UP)
+
ComponentInitialize()
return INITIALIZE_HINT_NORMAL
@@ -74,6 +97,10 @@
/turf/open/space/Assimilate_Air()
return
+//IT SHOULD RETURN NULL YOU MONKEY, WHY IN TARNATION WHAT THE FUCKING FUCK
+/turf/open/space/remove_air(amount)
+ return null
+
/turf/open/space/proc/update_starlight()
if(CONFIG_GET(flag/starlight))
for(var/t in RANGE_TURFS(1,src)) //RANGE_TURFS is in code\__HELPERS\game.dm
@@ -90,9 +117,8 @@
/turf/open/space/proc/CanBuildHere()
return TRUE
-/turf/open/space/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube)
- if(lube & FLYING_DOESNT_HELP)
- return ..()
+/turf/open/space/handle_slip()
+ return // no lube bullshit, this is space
/turf/open/space/attackby(obj/item/C, mob/user, params)
..()
@@ -107,15 +133,16 @@
return
if(L)
if(R.use(1))
+ qdel(L)
to_chat(user, "You construct a catwalk.")
- playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
+ playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
new/obj/structure/lattice/catwalk(src)
else
to_chat(user, "You need two rods to build a catwalk!")
return
if(R.use(1))
to_chat(user, "You construct a lattice.")
- playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
+ playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
ReplaceWithLattice()
else
to_chat(user, "You need one rod to build a lattice.")
@@ -126,7 +153,7 @@
var/obj/item/stack/tile/plasteel/S = C
if(S.use(1))
qdel(L)
- playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
+ playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
to_chat(user, "You build a floor.")
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 55d945535d..c2fee908fb 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -1,8 +1,11 @@
+GLOBAL_LIST_EMPTY(station_turfs)
+
+/// Any floor or wall. What makes up the station and the rest of the map.
/turf
icon = 'icons/turf/floors.dmi'
- level = 1
- vis_flags = VIS_INHERIT_PLANE|VIS_INHERIT_ID //when this be added to vis_contents of something it inherit something.plane and be associatet with something on clicking,
- //important for visualisation of turf in openspace and interraction with openspace that show you turf.
+ flags_1 = CAN_BE_DIRTY_1
+ vis_flags = VIS_INHERIT_ID|VIS_INHERIT_PLANE // Important for interaction with and visualization of openspace.
+ luminosity = 1
var/intact = 1
@@ -19,8 +22,6 @@
var/blocks_air = FALSE
- flags_1 = CAN_BE_DIRTY_1
-
var/list/image/blueprint_data //for the station blueprints, images of objects eg: pipes
var/explosion_level = 0 //for preventing explosion dodging
@@ -41,7 +42,13 @@
return FALSE
. = ..()
+/**
+ * Turf Initialize
+ *
+ * Doesn't call parent, see [/atom/proc/Initialize]
+ */
/turf/Initialize(mapload)
+ SHOULD_CALL_PARENT(FALSE)
if(flags_1 & INITIALIZED_1)
stack_trace("Warning: [src]([type]) initialized multiple times!")
flags_1 |= INITIALIZED_1
@@ -49,7 +56,7 @@
// by default, vis_contents is inherited from the turf that was here before
vis_contents.Cut()
- if(color)
+ if(color) // is this being used? This is here because parent isn't being called
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
assemble_baseturfs()
@@ -57,6 +64,7 @@
levelupdate()
if(smooth)
queue_smooth(src)
+
visibilityChanged()
for(var/atom/movable/AM in src)
@@ -76,11 +84,10 @@
var/turf/T = SSmapping.get_turf_above(src)
if(T)
T.multiz_turf_new(src, DOWN)
- SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, DOWN)
T = SSmapping.get_turf_below(src)
if(T)
T.multiz_turf_new(src, UP)
- SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, UP)
+
if (opacity)
has_opaque_atom = TRUE
@@ -112,8 +119,6 @@
var/turf/B = new world.turf(src)
for(var/A in B.contents)
qdel(A)
- for(var/I in B.vars)
- B.vars[I] = null
return
SSair.remove_from_active(src)
visibilityChanged()
@@ -122,12 +127,14 @@
requires_activation = FALSE
..()
-/turf/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
+/turf/on_attack_hand(mob/user)
user.Move_Pulled(src)
/turf/proc/multiz_turf_del(turf/T, dir)
+ // SEND_SIGNAL(src, COMSIG_TURF_MULTIZ_DEL, T, dir)
/turf/proc/multiz_turf_new(turf/T, dir)
+ SEND_SIGNAL(src, COMSIG_TURF_MULTIZ_NEW, T, dir)
//zPassIn doesn't necessarily pass an atom!
//direction is direction of travel of air
@@ -158,13 +165,14 @@
prev_turf.visible_message("[mov_name] falls through [prev_turf]!")
if(flags & FALL_INTERCEPTED)
return
- if(zFall(A, ++levels))
+ if(zFall(A, levels + 1))
return FALSE
A.visible_message("[A] crashes into [src]!")
A.onZImpact(src, levels)
return TRUE
/turf/proc/can_zFall(atom/movable/A, levels = 1, turf/target)
+ SHOULD_BE_PURE(TRUE)
return zPassOut(A, DOWN, target) && target.zPassIn(A, DOWN, src)
/turf/proc/zFall(atom/movable/A, levels = 1, force = FALSE)
@@ -217,49 +225,54 @@
stack_trace("Non movable passed to turf CanPass : [mover]")
return FALSE
+//There's a lot of QDELETED() calls here if someone can figure out how to optimize this but not runtime when something gets deleted by a Bump/CanPass/Cross call, lemme know or go ahead and fix this mess - kevinz000
/turf/Enter(atom/movable/mover, atom/oldloc)
// Do not call ..()
// Byond's default turf/Enter() doesn't have the behaviour we want with Bump()
// By default byond will call Bump() on the first dense object in contents
// Here's hoping it doesn't stay like this for years before we finish conversion to step_
var/atom/firstbump
- if(!CanPass(mover, src))
- firstbump = src
- else
+ var/canPassSelf = CanPass(mover, src)
+ if(canPassSelf || (mover.movement_type & UNSTOPPABLE))
for(var/i in contents)
+ if(QDELETED(mover))
+ return FALSE //We were deleted, do not attempt to proceed with movement.
if(i == mover || i == mover.loc) // Multi tile objects and moving out of other objects
continue
- if(QDELETED(mover))
- break
var/atom/movable/thing = i
if(!thing.Cross(mover))
- if(CHECK_BITFIELD(mover.movement_type, UNSTOPPABLE))
+ if(QDELETED(mover)) //Mover deleted from Cross/CanPass, do not proceed.
+ return FALSE
+ if((mover.movement_type & UNSTOPPABLE))
mover.Bump(thing)
continue
else
if(!firstbump || ((thing.layer > firstbump.layer || thing.flags_1 & ON_BORDER_1) && !(firstbump.flags_1 & ON_BORDER_1)))
firstbump = thing
+ if(QDELETED(mover)) //Mover deleted from Cross/CanPass/Bump, do not proceed.
+ return FALSE
+ if(!canPassSelf) //Even if mover is unstoppable they need to bump us.
+ firstbump = src
if(firstbump)
- if(!QDELETED(mover))
- mover.Bump(firstbump)
- return CHECK_BITFIELD(mover.movement_type, UNSTOPPABLE)
+ mover.Bump(firstbump)
+ return (mover.movement_type & UNSTOPPABLE)
return TRUE
/turf/Exit(atom/movable/mover, atom/newloc)
. = ..()
- if(!.)
+ if(!. || QDELETED(mover))
return FALSE
for(var/i in contents)
- if(QDELETED(mover))
- break
if(i == mover)
continue
var/atom/movable/thing = i
if(!thing.Uncross(mover, newloc))
if(thing.flags_1 & ON_BORDER_1)
mover.Bump(thing)
- if(!CHECK_BITFIELD(mover.movement_type, UNSTOPPABLE))
+ if(!(mover.movement_type & UNSTOPPABLE))
return FALSE
+ if(QDELETED(mover))
+ return FALSE //We were deleted.
/turf/Entered(atom/movable/AM)
..()
@@ -271,6 +284,7 @@
has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case.
reconsider_lights()
+
/turf/open/Entered(atom/movable/AM)
..()
//melting
@@ -278,7 +292,6 @@
var/obj/O = AM
if(O.obj_flags & FROZEN)
O.make_unfrozen()
-
if(!AM.zfalling)
zFall(AM)
@@ -336,14 +349,13 @@
/turf/proc/levelupdate()
for(var/obj/O in src)
- if(O.level == 1 && (O.flags_1 & INITIALIZED_1))
- O.hide(src.intact)
+ if(O.flags_1 & INITIALIZED_1)
+ // SEND_SIGNAL(O, COMSIG_OBJ_HIDE, intact)
+ O.hide(intact)
// override for space turfs, since they should never hide anything
/turf/open/space/levelupdate()
- for(var/obj/O in src)
- if(O.level == 1 && (O.flags_1 & INITIALIZED_1))
- O.hide(0)
+ return
// Removes all signs of lattice on the pos of the turf -Donkieyo
/turf/proc/RemoveLattice()
@@ -368,13 +380,13 @@
if(.)
return
if(length(src_object.contents()))
- to_chat(user, "You start dumping out the contents...")
- if(!do_after(user,20,target=src_object.parent))
+ to_chat(usr, "You start dumping out the contents...")
+ if(!do_after(usr,20,target=src_object.parent))
return FALSE
var/list/things = src_object.contents()
var/datum/progressbar/progress = new(user, things.len, src)
- while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(src_object, /datum/component/storage.proc/mass_remove_from_storage, src, things, progress)))
+ while (do_after(usr, 1 SECONDS, TRUE, src, FALSE, CALLBACK(src_object, /datum/component/storage.proc/mass_remove_from_storage, src, things, progress)))
stoplag(1)
qdel(progress)
@@ -494,10 +506,8 @@
I.loc = src
I.setDir(AM.dir)
I.alpha = 128
-
LAZYADD(blueprint_data, I)
-
/turf/proc/add_blueprints_preround(atom/movable/AM)
if(!SSticker.HasRoundStarted())
add_blueprints(AM)
@@ -531,7 +541,7 @@
if(!forced)
return
if(has_gravity(src))
- playsound(src, "bodyfall", 50, 1)
+ playsound(src, "bodyfall", 50, TRUE)
faller.drop_all_held_items()
/turf/proc/photograph(limit=20)
diff --git a/code/game/world.dm b/code/game/world.dm
index 42c90d8be1..4369ca6668 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -1,21 +1,46 @@
#define RESTART_COUNTER_PATH "data/round_counter.txt"
GLOBAL_VAR(restart_counter)
+
GLOBAL_VAR_INIT(tgs_initialized, FALSE)
GLOBAL_VAR(topic_status_lastcache)
GLOBAL_LIST(topic_status_cache)
-//This happens after the Master subsystem new(s) (it's a global datum)
-//So subsystems globals exist, but are not initialised
+/**
+ * World creation
+ *
+ * Here is where a round itself is actually begun and setup.
+ * * db connection setup
+ * * config loaded from files
+ * * loads admins
+ * * Sets up the dynamic menu system
+ * * and most importantly, calls initialize on the master subsystem, starting the game loop that causes the rest of the game to begin processing and setting up
+ *
+ *
+ * Nothing happens until something moves. ~Albert Einstein
+ *
+ * For clarity, this proc gets triggered later in the initialization pipeline, it is not the first thing to happen, as it might seem.
+ *
+ * Initialization Pipeline:
+ * Global vars are new()'ed, (including config, glob, and the master controller will also new and preinit all subsystems when it gets new()ed)
+ * Compiled in maps are loaded (mainly centcom). all areas/turfs/objs/mobs(ATOMs) in these maps will be new()ed
+ * world/New() (You are here)
+ * Once world/New() returns, client's can connect.
+ * 1 second sleep
+ * Master Controller initialization.
+ * Subsystem initialization.
+ * Non-compiled-in maps are maploaded, all atoms are new()ed
+ * All atoms in both compiled and uncompiled maps are initialized()
+ */
/world/New()
if (fexists(EXTOOLS))
call(EXTOOLS, "maptick_initialize")()
- #ifdef EXTOOLS_LOGGING
+#ifdef EXTOOLS_LOGGING
call(EXTOOLS, "init_logging")()
else
CRASH("[EXTOOLS] does not exist!")
- #endif
+#endif
enable_debugger()
#ifdef REFERENCE_TRACKING
enable_reference_tracking()
@@ -23,12 +48,11 @@ GLOBAL_LIST(topic_status_cache)
world.Profile(PROFILE_START)
- log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
-
GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
- make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
+ log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
+ make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
GLOB.revdata = new
@@ -36,6 +60,9 @@ GLOBAL_LIST(topic_status_cache)
config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER])
+ load_admins()
+ load_mentors()
+
//SetupLogs depends on the RoundID, so lets check
//DB schema and set RoundID if we can
SSdbcore.CheckSchemaVersion()
@@ -49,14 +76,9 @@ GLOBAL_LIST(topic_status_cache)
world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them.
#endif
- load_admins()
- load_mentors()
LoadVerbs(/datum/verbs/menu)
if(CONFIG_GET(flag/usewhitelist))
load_whitelist()
- LoadBans()
- initialize_global_loadout_items()
- reload_custom_roundstart_items_list()//Cit change - loads donator items. Remind me to remove when I port over bay's loadout system
GLOB.timezoneOffset = text2num(time2text(0,"hh")) * 36000
@@ -67,10 +89,15 @@ GLOBAL_LIST(topic_status_cache)
if(NO_INIT_PARAMETER in params)
return
+ LoadBans()
+ initialize_global_loadout_items()
+ reload_custom_roundstart_items_list()//Cit change - loads donator items. Remind me to remove when I port over bay's loadout system
+
Master.Initialize(10, FALSE, TRUE)
- if(TEST_RUN_PARAMETER in params)
- HandleTestRun()
+ #ifdef UNIT_TESTS
+ HandleTestRun()
+ #endif
/world/proc/InitTgs()
TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED)
@@ -88,7 +115,7 @@ GLOBAL_LIST(topic_status_cache)
#else
cb = VARSET_CALLBACK(SSticker, force_ending, TRUE)
#endif
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/addtimer, cb, 10 SECONDS))
+ SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/_addtimer, cb, 10 SECONDS))
/world/proc/SetupLogs()
var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER]
@@ -136,7 +163,7 @@ GLOBAL_LIST(topic_status_cache)
#ifdef UNIT_TESTS
- GLOB.test_log = file("[GLOB.log_directory]/tests.log")
+ GLOB.test_log = "[GLOB.log_directory]/tests.log"
start_log(GLOB.test_log)
#endif
start_log(GLOB.world_game_log)
@@ -172,7 +199,7 @@ GLOBAL_LIST(topic_status_cache)
if(!SSfail2topic)
return "Server not initialized."
- else if(SSfail2topic.IsRateLimited(addr))
+ if(SSfail2topic.IsRateLimited(addr))
return "Rate limited."
if(length(T) > CONFIG_GET(number/topic_max_size))
@@ -243,9 +270,10 @@ GLOBAL_LIST(topic_status_cache)
TgsReboot()
- if(TEST_RUN_PARAMETER in params)
- FinishTestRun()
- return
+ #ifdef UNIT_TESTS
+ FinishTestRun()
+ return
+ #endif
if(TgsAvailable())
var/do_hard_reboot
@@ -280,11 +308,11 @@ GLOBAL_LIST(topic_status_cache)
var/list/features = list()
- /*if(GLOB.master_mode) CIT CHANGE - hides the gamemode from the hub entry, removes some useless info from the hub entry
- features += GLOB.master_mode
+ // if(GLOB.master_mode)
+ // features += GLOB.master_mode
- if (!GLOB.enter_allowed)
- features += "closed"*/
+ // if (!GLOB.enter_allowed)
+ // features += "closed"
var/s = ""
var/hostedby
@@ -292,25 +320,22 @@ GLOBAL_LIST(topic_status_cache)
var/server_name = CONFIG_GET(string/servername)
if (server_name)
s += "[server_name] — "
- /*features += "[CONFIG_GET(flag/norespawn) ? "no " : ""]respawn" CIT CHANGE - removes some useless info from the hub entry
- if(CONFIG_GET(flag/allow_vote_mode))
- features += "vote"
- if(CONFIG_GET(flag/allow_ai))
- features += "AI allowed"*/
+ // features += "[CONFIG_GET(flag/norespawn) ? "no " : ""]respawn"
+ // if(CONFIG_GET(flag/allow_vote_mode))
+ // features += "vote"
+ // if(CONFIG_GET(flag/allow_ai))
+ // features += "AI allowed"
hostedby = CONFIG_GET(string/hostedby)
s += "[station_name()]";
s += " ("
- s += "" //Change this to wherever you want the hub to link to. CIT CHANGE - links to cit's website on the hub
- s += "Citadel" //Replace this with something else. Or ever better, delete it and uncomment the game version. CIT CHANGE - modifies the hub entry link
+ s += "" //Change this to wherever you want the hub to link to.
+ s += "Citadel" //Replace this with something else. Or ever better, delete it and uncomment the game version.
s += ""
s += ")\]" //CIT CHANGE - encloses the server title in brackets to make the hub entry fancier
s += "
[CONFIG_GET(string/servertagline)]
" //CIT CHANGE - adds a tagline!
- var/n = 0
- for (var/mob/M in GLOB.player_list)
- if (M.client)
- n++
+ var/players = GLOB.clients.len
if(SSmapping.config) // this just stops the runtime, honk.
features += "[SSmapping.config.map_name]" //CIT CHANGE - makes the hub entry display the current map
@@ -318,16 +343,23 @@ GLOBAL_LIST(topic_status_cache)
if(NUM2SECLEVEL(GLOB.security_level))//CIT CHANGE - makes the hub entry show the security level
features += "[NUM2SECLEVEL(GLOB.security_level)] alert"
- if (n > 1)
- features += "~[n] players"
- else if (n > 0)
- features += "~[n] player"
+ var/popcaptext = ""
+ var/popcap = max(CONFIG_GET(number/extreme_popcap), CONFIG_GET(number/hard_popcap), CONFIG_GET(number/soft_popcap))
+ if (popcap)
+ popcaptext = "/[popcap]"
+
+ if (players > 1)
+ features += "[players][popcaptext] players"
+ else if (players > 0)
+ features += "[players][popcaptext] player"
+
+ game_state = (CONFIG_GET(number/extreme_popcap) && players >= CONFIG_GET(number/extreme_popcap)) //tells the hub if we are full
if (!host && hostedby)
features += "hosted by [hostedby]"
if (features)
- s += "\[[jointext(features, ", ")]" //CIT CHANGE - replaces the colon here with a left bracket
+ s += "\[[jointext(features, ", ")]"
status = s
@@ -346,4 +378,28 @@ GLOBAL_LIST(topic_status_cache)
SSidlenpcpool.MaxZChanged()
world.refresh_atmos_grid()
+/// Extools atmos
/world/proc/refresh_atmos_grid()
+
+/world/proc/change_fps(new_value = 20)
+ if(new_value <= 0)
+ CRASH("change_fps() called with [new_value] new_value.")
+ if(fps == new_value)
+ return //No change required.
+
+ fps = new_value
+ on_tickrate_change()
+
+
+/world/proc/change_tick_lag(new_value = 0.5)
+ if(new_value <= 0)
+ CRASH("change_tick_lag() called with [new_value] new_value.")
+ if(tick_lag == new_value)
+ return //No change required.
+
+ tick_lag = new_value
+ on_tickrate_change()
+
+
+/world/proc/on_tickrate_change()
+ SStimer?.reset_buckets()
diff --git a/code/modules/actionspeed/_actionspeed_modifier.dm b/code/modules/actionspeed/_actionspeed_modifier.dm
index fe244e6653..a2870be6f4 100644
--- a/code/modules/actionspeed/_actionspeed_modifier.dm
+++ b/code/modules/actionspeed/_actionspeed_modifier.dm
@@ -69,7 +69,7 @@ GLOBAL_LIST_EMPTY(actionspeed_modification_cache)
return TRUE
remove_actionspeed_modifier(existing, FALSE)
if(length(actionspeed_modification))
- BINARY_INSERT(type_or_datum.id, actionspeed_modification, datum/actionspeed_modifier, type_or_datum, priority, COMPARE_VALUE)
+ BINARY_INSERT(type_or_datum.id, actionspeed_modification, /datum/actionspeed_modifier, type_or_datum, priority, COMPARE_VALUE)
LAZYSET(actionspeed_modification, type_or_datum.id, type_or_datum)
if(update)
update_actionspeed()
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 4ddb92ab19..63facade2e 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -226,6 +226,14 @@
key_cache[key] = 0
return .
+/proc/restore_stickybans()
+ for (var/banned_ckey in GLOB.stickybanadmintexts)
+ world.SetConfig("ban", banned_ckey, GLOB.stickybanadmintexts[banned_ckey])
+ GLOB.stickybanadminexemptions = list()
+ GLOB.stickybanadmintexts = list()
+ if (GLOB.stickbanadminexemptiontimerid)
+ deltimer(GLOB.stickbanadminexemptiontimerid)
+ GLOB.stickbanadminexemptiontimerid = null
#undef STICKYBAN_MAX_MATCHES
#undef STICKYBAN_MAX_EXISTING_USER_MATCHES
diff --git a/code/modules/admin/sound_emitter.dm b/code/modules/admin/sound_emitter.dm
index 56c778dc85..ad9c995aa1 100644
--- a/code/modules/admin/sound_emitter.dm
+++ b/code/modules/admin/sound_emitter.dm
@@ -52,11 +52,9 @@
edit_emitter(user)
/obj/effect/sound_emitter/AltClick(mob/user)
- . = ..()
if(check_rights_for(user.client, R_SOUNDS))
activate(user)
- to_chat(user, "Sound emitter activated.")
- return TRUE
+ to_chat(user, "Sound emitter activated.", confidential = TRUE)
/obj/effect/sound_emitter/proc/edit_emitter(mob/user)
var/dat = ""
@@ -84,20 +82,20 @@
if(!new_label)
return
maptext = new_label
- to_chat(user, "Label set to [maptext].")
+ to_chat(user, "Label set to [maptext].", confidential = TRUE)
if(href_list["edit_sound_file"])
var/new_file = input(user, "Choose a sound file.", "Sound Emitter") as null|sound
if(!new_file)
return
sound_file = new_file
- to_chat(user, "New sound file set to [sound_file].")
+ to_chat(user, "New sound file set to [sound_file].", confidential = TRUE)
if(href_list["edit_volume"])
var/new_volume = input(user, "Choose a volume.", "Sound Emitter", sound_volume) as null|num
if(isnull(new_volume))
return
new_volume = clamp(new_volume, 0, 100)
sound_volume = new_volume
- to_chat(user, "Volume set to [sound_volume]%.")
+ to_chat(user, "Volume set to [sound_volume]%.", confidential = TRUE)
if(href_list["edit_mode"])
var/new_mode
var/mode_list = list("Local (normal sound)" = SOUND_EMITTER_LOCAL, "Direct (not affected by environment/location)" = SOUND_EMITTER_DIRECT)
@@ -105,7 +103,7 @@
if(!new_mode)
return
motus_operandi = mode_list[new_mode]
- to_chat(user, "Mode set to [motus_operandi].")
+ to_chat(user, "Mode set to [motus_operandi].", confidential = TRUE)
if(href_list["edit_range"])
var/new_range
var/range_list = list("Radius (all mobs within a radius)" = SOUND_EMITTER_RADIUS, "Z-Level (all mobs on the same z)" = SOUND_EMITTER_ZLEVEL, "Global (all players)" = SOUND_EMITTER_GLOBAL)
@@ -113,14 +111,14 @@
if(!new_range)
return
emitter_range = range_list[new_range]
- to_chat(user, "Range set to [emitter_range].")
+ to_chat(user, "Range set to [emitter_range].", confidential = TRUE)
if(href_list["edit_radius"])
var/new_radius = input(user, "Choose a radius.", "Sound Emitter", sound_volume) as null|num
if(isnull(new_radius))
return
new_radius = clamp(new_radius, 0, 127)
play_radius = new_radius
- to_chat(user, "Audible radius set to [play_radius].")
+ to_chat(user, "Audible radius set to [play_radius].", confidential = TRUE)
if(href_list["play"])
activate(user)
edit_emitter(user) //Refresh the UI to see our changes
diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm
index df6a9b89ad..ef0bfe8e70 100644
--- a/code/modules/admin/stickyban.dm
+++ b/code/modules/admin/stickyban.dm
@@ -7,7 +7,7 @@
if ("add")
var/list/ban = list()
var/ckey
- ban["admin"] = usr.key
+ ban["admin"] = usr.ckey
ban["type"] = list("sticky")
ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
@@ -21,7 +21,8 @@
ban["ckey"] = ckey
if (get_stickyban_from_ckey(ckey))
- to_chat(usr, "Error: Can not add a stickyban: User already has a current sticky ban")
+ to_chat(usr, "Error: Can not add a stickyban: User already has a current sticky ban", confidential = TRUE)
+ return
if (data["reason"])
ban["message"] = data["reason"]
@@ -31,7 +32,26 @@
return
ban["message"] = "[reason]"
+ if(SSdbcore.Connect()) // todo: second wave
+ // var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
+ // INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
+ // VALUES (:ckey, :message, :banning_admin)
+ // "}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
+ var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
+ VALUES ([ckey], [ban["message"]], [usr.ckey])
+ "})
+ if (query_create_stickyban.warn_execute())
+ ban["fromdb"] = TRUE
+ qdel(query_create_stickyban)
+
world.SetConfig("ban",ckey,list2stickyban(ban))
+ ban = stickyban2list(list2stickyban(ban))
+ ban["matches_this_round"] = list()
+ ban["existing_user_matches_this_round"] = list()
+ ban["admin_matches_this_round"] = list()
+ ban["pending_matches_this_round"] = list()
+ SSstickyban.cache[ckey] = ban
log_admin_private("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
message_admins("[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
@@ -43,14 +63,29 @@
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
- to_chat(usr, "Error: No sticky ban for [ckey] found!")
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
return
if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No")
return
if (!get_stickyban_from_ckey(ckey))
- to_chat(usr, "Error: The ban disappeared.")
+ to_chat(usr, "Error: The ban disappeared.", confidential = TRUE)
return
world.SetConfig("ban",ckey, null)
+ SSstickyban.cache -= ckey
+
+ if (SSdbcore.Connect())
+ // SSdbcore.QuerySelect(list(
+ // SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
+ // SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
+ // SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
+ // SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
+ // ), warn = TRUE, qdel = TRUE)
+ SSdbcore.QuerySelect(list(
+ SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = [ckey]"),
+ SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey]"),
+ SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = [ckey]"),
+ SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = [ckey]")
+ ), warn = TRUE, qdel = TRUE)
log_admin_private("[key_name(usr)] removed [ckey]'s stickyban")
message_admins("[key_name_admin(usr)] removed [ckey]'s stickyban")
@@ -64,42 +99,45 @@
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
- to_chat(usr, "Error: No sticky ban for [ckey] found!")
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
return
- var/found = 0
- //we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
- for (var/key in ban["keys"])
- if (ckey(key) == alt)
- found = 1
- break
-
- if (!found)
- to_chat(usr, "Error: [alt] is not linked to [ckey]'s sticky ban!")
+ var/key = LAZYACCESS(ban["keys"], alt)
+ if (!key)
+ to_chat(usr, "Error: [alt] is not linked to [ckey]'s sticky ban!", confidential = TRUE)
return
- if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
+ if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them, Use \[E] to exempt them","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
- to_chat(usr, "Error: The ban disappeared.")
+ to_chat(usr, "Error: The ban disappeared.", confidential = TRUE)
return
- found = 0
- for (var/key in ban["keys"])
- if (ckey(key) == alt)
- ban["keys"] -= key
- found = 1
- break
+ key = LAZYACCESS(ban["keys"], alt)
- if (!found)
- to_chat(usr, "Error: [alt] link to [ckey]'s sticky ban disappeared.")
+ if (!key)
+ to_chat(usr, "Error: [alt] link to [ckey]'s sticky ban disappeared.", confidential = TRUE)
return
+ LAZYREMOVE(ban["keys"], alt)
world.SetConfig("ban",ckey,list2stickyban(ban))
+ SSstickyban.cache[ckey] = ban
+
+ if (SSdbcore.Connect())
+ // var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
+ // "DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
+ // list("ckey" = ckey, "alt" = alt)
+ // )
+ var/datum/DBQuery/query_remove_stickyban_alt = SSdbcore.NewQuery(
+ "DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey] AND matched_ckey = [alt]"
+ )
+ query_remove_stickyban_alt.warn_execute()
+ qdel(query_remove_stickyban_alt)
+
log_admin_private("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
message_admins("[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban")
@@ -109,7 +147,7 @@
var/ckey = data["ckey"]
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
- to_chat(usr, "Error: No sticky ban for [ckey] found!")
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
return
var/oldreason = ban["message"]
var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null
@@ -118,28 +156,203 @@
//we have to do this again incase something changed while we waited for input
ban = get_stickyban_from_ckey(ckey)
if (!ban)
- to_chat(usr, "Error: The ban disappeared.")
+ to_chat(usr, "Error: The ban disappeared.", confidential = TRUE)
return
ban["message"] = "[reason]"
world.SetConfig("ban",ckey,list2stickyban(ban))
+ SSstickyban.cache[ckey] = ban
+
+ if (SSdbcore.Connect())
+ // var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
+ // "UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
+ // list("reason" = reason, "ckey" = ckey)
+ // )
+ var/datum/DBQuery/query_edit_stickyban = SSdbcore.NewQuery(
+ "UPDATE [format_table_name("stickyban")] SET reason = [reason] WHERE ckey = [ckey]"
+ )
+ query_edit_stickyban.warn_execute()
+ qdel(query_edit_stickyban)
+
log_admin_private("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
message_admins("[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
+ if ("exempt")
+ if (!data["ckey"])
+ return
+ var/ckey = data["ckey"]
+ if (!data["alt"])
+ return
+ var/alt = ckey(data["alt"])
+ var/ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
+ return
+
+ var/key = LAZYACCESS(ban["keys"], alt)
+ if (!key)
+ to_chat(usr, "Error: [alt] is not linked to [ckey]'s sticky ban!", confidential = TRUE)
+ return
+
+ if (alert("Are you sure you want to exempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
+ return
+
+ //we have to do this again incase something changes
+ ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ to_chat(usr, "Error: The ban disappeared.", confidential = TRUE)
+ return
+
+ key = LAZYACCESS(ban["keys"], alt)
+
+ if (!key)
+ to_chat(usr, "Error: [alt]'s link to [ckey]'s sticky ban disappeared.", confidential = TRUE)
+ return
+ LAZYREMOVE(ban["keys"], alt)
+ key["exempt"] = TRUE
+ LAZYSET(ban["whitelist"], alt, key)
+
+ world.SetConfig("ban",ckey,list2stickyban(ban))
+
+ SSstickyban.cache[ckey] = ban
+
+ if (SSdbcore.Connect())
+ // var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
+ // "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
+ // list("ckey" = ckey, "alt" = alt)
+ // )
+ var/datum/DBQuery/query_exempt_stickyban_alt = SSdbcore.NewQuery(
+ "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
+ )
+ query_exempt_stickyban_alt.warn_execute()
+ qdel(query_exempt_stickyban_alt)
+
+ log_admin_private("[key_name(usr)] has exempted [alt] from [ckey]'s sticky ban")
+ message_admins("[key_name_admin(usr)] has exempted [alt] from [ckey]'s sticky ban")
+
+ if ("unexempt")
+ if (!data["ckey"])
+ return
+ var/ckey = data["ckey"]
+ if (!data["alt"])
+ return
+ var/alt = ckey(data["alt"])
+ var/ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
+ return
+
+ var/key = LAZYACCESS(ban["whitelist"], alt)
+ if (!key)
+ to_chat(usr, "Error: [alt] is not exempt from [ckey]'s sticky ban!", confidential = TRUE)
+ return
+
+ if (alert("Are you sure you want to unexempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
+ return
+
+ //we have to do this again incase something changes
+ ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ to_chat(usr, "Error: The ban disappeared.", confidential = TRUE)
+ return
+
+ key = LAZYACCESS(ban["whitelist"], alt)
+ if (!key)
+ to_chat(usr, "Error: [alt]'s exemption from [ckey]'s sticky ban disappeared.", confidential = TRUE)
+ return
+
+ LAZYREMOVE(ban["whitelist"], alt)
+ key["exempt"] = FALSE
+ LAZYSET(ban["keys"], alt, key)
+
+ world.SetConfig("ban",ckey,list2stickyban(ban))
+
+ SSstickyban.cache[ckey] = ban
+
+ if (SSdbcore.Connect())
+ // var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
+ // "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
+ // list("ckey" = ckey, "alt" = alt)
+ // )
+ var/datum/DBQuery/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
+ "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
+ )
+ query_unexempt_stickyban_alt.warn_execute()
+ qdel(query_unexempt_stickyban_alt)
+
+ log_admin_private("[key_name(usr)] has unexempted [alt] from [ckey]'s sticky ban")
+ message_admins("[key_name_admin(usr)] has unexempted [alt] from [ckey]'s sticky ban")
+
+ if ("timeout")
+ if (!data["ckey"])
+ return
+ if (!SSdbcore.Connect())
+ to_chat(usr, "No database connection!", confidential = TRUE)
+ return
+
+ var/ckey = data["ckey"]
+
+ if (alert("Are you sure you want to put [ckey]'s stickyban on timeout until next round (or removed)?","Are you sure","Yes","No") == "No")
+ return
+ var/ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
+ return
+
+ ban["timeout"] = TRUE
+
+ world.SetConfig("ban", ckey, null)
+
+ var/cachedban = SSstickyban.cache[ckey]
+ if (cachedban)
+ cachedban["timeout"] = TRUE
+
+ log_admin_private("[key_name(usr)] has put [ckey]'s sticky ban on timeout.")
+ message_admins("[key_name_admin(usr)] has put [ckey]'s sticky ban on timeout.")
+
+ if ("untimeout")
+ if (!data["ckey"])
+ return
+ if (!SSdbcore.Connect())
+ to_chat(usr, "No database connection!", confidential = TRUE)
+ return
+ var/ckey = data["ckey"]
+
+ if (alert("Are you sure you want to lift the timeout on [ckey]'s stickyban?","Are you sure","Yes","No") == "No")
+ return
+
+ var/ban = get_stickyban_from_ckey(ckey)
+ var/cachedban = SSstickyban.cache[ckey]
+ if (cachedban)
+ cachedban["timeout"] = FALSE
+ if (!ban)
+ if (!cachedban)
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
+ return
+ ban = cachedban
+
+ ban["timeout"] = FALSE
+
+ world.SetConfig("ban",ckey,list2stickyban(ban))
+
+ log_admin_private("[key_name(usr)] has taken [ckey]'s sticky ban off of timeout.")
+ message_admins("[key_name_admin(usr)] has taken [ckey]'s sticky ban off of timeout.")
+
+
if ("revert")
if (!data["ckey"])
return
var/ckey = data["ckey"]
- if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start?","Are you sure","Yes","No") == "No")
+ if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start (or last edit)?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
- to_chat(usr, "Error: No sticky ban for [ckey] found!")
+ to_chat(usr, "Error: No sticky ban for [ckey] found!", confidential = TRUE)
return
var/cached_ban = SSstickyban.cache[ckey]
if (!cached_ban)
- to_chat(usr, "Error: No cached sticky ban for [ckey] found!")
+ to_chat(usr, "Error: No cached sticky ban for [ckey] found!", confidential = TRUE)
world.SetConfig("ban",ckey,null)
log_admin_private("[key_name(usr)] has reverted [ckey]'s sticky ban to its state at round start.")
@@ -150,14 +363,22 @@
world.SetConfig("ban",ckey,list2stickyban(cached_ban))
-/datum/admins/proc/stickyban_gethtml(ckey, ban)
- . = {"
+/datum/admins/proc/stickyban_gethtml(ckey)
+ var/ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ return
+ var/timeout
+ if (SSdbcore.Connect())
+ timeout = "\[[(ban["timeout"] ? "untimeout" : "timeout" )]\]"
+ else
+ timeout = "\[revert\]"
+ . = list({"
\[-\]
- \[revert\]
+ [timeout]
[ckey]
"
[ban["message"]] \[Edit\]
- "}
+ "})
if (ban["admin"])
. += "[ban["admin"]]
"
else
@@ -166,19 +387,24 @@
for (var/key in ban["keys"])
if (ckey(key) == ckey)
continue
- . += "
\n"
- banhtml += stickyban_gethtml(ckey,ban)
+ banhtml += stickyban_gethtml(ckey)
var/html = {"
@@ -186,22 +412,49 @@
All Sticky Bans:
\[+\]
- [banhtml]
+ [banhtml.Join("")]
"}
usr << browse(html,"window=stickybans;size=700x400")
-/proc/get_stickyban_from_ckey(var/ckey)
+/proc/sticky_banned_ckeys()
+ if (SSdbcore.Connect() || length(SSstickyban.dbcache))
+ if (SSstickyban.dbcacheexpire < world.time)
+ SSstickyban.Populatedbcache()
+ if (SSstickyban.dbcacheexpire)
+ return SSstickyban.dbcache.Copy()
+
+ return sortList(world.GetConfig("ban"))
+
+
+/proc/get_stickyban_from_ckey(ckey)
+ . = list()
if (!ckey)
return null
- ckey = ckey(ckey)
- . = null
- for (var/key in world.GetConfig("ban"))
- if (ckey(key) == ckey)
- . = stickyban2list(world.GetConfig("ban",key))
- break
+ if (SSdbcore.Connect() || length(SSstickyban.dbcache))
+ if (SSstickyban.dbcacheexpire < world.time)
+ SSstickyban.Populatedbcache()
+ if (SSstickyban.dbcacheexpire)
+ . = SSstickyban.dbcache[ckey]
+ //reset the cache incase its a newer ban (but only if we didn't update the cache recently)
+ if (!. && SSstickyban.dbcacheexpire != world.time+STICKYBAN_DB_CACHE_TIME)
+ SSstickyban.dbcacheexpire = 1
+ SSstickyban.Populatedbcache()
+ . = SSstickyban.dbcache[ckey]
+ if (.)
+ var/list/cachedban = SSstickyban.cache["[ckey]"]
+ if (cachedban)
+ .["timeout"] = cachedban["timeout"]
-/proc/stickyban2list(var/ban)
+ .["fromdb"] = TRUE
+ return
+
+ . = stickyban2list(world.GetConfig("ban", ckey)) || stickyban2list(world.GetConfig("ban", ckey(ckey))) || list()
+
+ if (!length(.))
+ return null
+
+/proc/stickyban2list(ban, strictdb = TRUE)
if (!ban)
return null
. = params2list(ban)
@@ -212,30 +465,40 @@
var/ckey = ckey(key)
ckeys[ckey] = ckey //to make searching faster.
.["keys"] = ckeys
+ if (.["whitelist"])
+ var/keys = splittext(.["whitelist"], ",")
+ var/ckeys = list()
+ for (var/key in keys)
+ var/ckey = ckey(key)
+ ckeys[ckey] = ckey //to make searching faster.
+ .["whitelist"] = ckeys
.["type"] = splittext(.["type"], ",")
.["IP"] = splittext(.["IP"], ",")
.["computer_id"] = splittext(.["computer_id"], ",")
+ . -= "fromdb"
-/proc/list2stickyban(var/list/ban)
+/proc/list2stickyban(list/ban)
if (!ban || !islist(ban))
return null
. = ban.Copy()
if (.["keys"])
.["keys"] = jointext(.["keys"], ",")
+ if (.["IP"])
+ .["IP"] = jointext(.["IP"], ",")
+ if (.["computer_id"])
+ .["computer_id"] = jointext(.["computer_id"], ",")
+ if (.["whitelist"])
+ .["whitelist"] = jointext(.["whitelist"], ",")
if (.["type"])
.["type"] = jointext(.["type"], ",")
- //internal tracking only, shouldn't be stored
+ . -= "reverting"
+ . -= "matches_this_round"
. -= "existing_user_matches_this_round"
. -= "admin_matches_this_round"
- . -= "matches_this_round"
- . -= "reverting"
+ . -= "pending_matches_this_round"
- //storing these can sometimes cause sticky bans to start matching everybody
- // and isn't even needed for sticky ban matching, as the hub tracks these separately
- . -= "IP"
- . -= "computer_id"
. = list2params(.)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 97d912cb9e..f2c10ecc1c 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -782,7 +782,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
if(temp.vars.Find(v))
temp.vars[v] = SDQL_expression(d, set_list[sets])
else
- temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))
+ temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]), TRUE)
break
if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client)))
temp = temp.vars[v]
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 4348ae7661..45d05c70b9 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -174,7 +174,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//is_bwoink is TRUE if this ticket was started by an admin PM
/datum/admin_help/New(msg, client/C, is_bwoink)
//clean the input msg
- msg = sanitize(copytext_char(msg,1,MAX_MESSAGE_LEN))
+ msg = copytext_char(msg,1,MAX_MESSAGE_LEN)
if(!msg || !C || !C.mob)
qdel(src)
return
@@ -263,7 +263,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//message from the initiator without a target, all admins will see this
//won't bug irc
/datum/admin_help/proc/MessageNoRecipient(msg)
- msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
+ msg = copytext_char(msg, 1, MAX_MESSAGE_LEN)
var/ref_src = "[REF(src)]"
//Message to be sent to all admins
var/admin_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [keywords_lookup(msg)]"
@@ -523,7 +523,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(handle_spam_prevention(msg,MUTE_ADMINHELP))
return
- msg = trim(msg)
+ msg = sanitize(trim(msg))
if(!msg)
return
diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm
index ef56b4cd2a..2f89dc82eb 100644
--- a/code/modules/admin/verbs/adminjump.dm
+++ b/code/modules/admin/verbs/adminjump.dm
@@ -158,3 +158,19 @@
else
to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/// Proc to hook user-enacted teleporting behavior and keep logging of the event.
+/atom/movable/proc/admin_teleport(atom/new_location, message = TRUE)
+ if(isnull(new_location))
+ log_admin("[key_name(usr)] teleported [key_name(src)] to nullspace")
+ moveToNullspace()
+ else
+ log_admin("[key_name(usr)] teleported [key_name(src)] to [AREACOORD(loc)]")
+ forceMove(new_location)
+
+/mob/admin_teleport(atom/new_location, message = TRUE)
+ var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(src)] to [isnull(new_location) ? "nullspace" : ADMIN_VERBOSEJMP(loc)]"
+ if(message)
+ message_admins(msg)
+ admin_ticket_log(src, msg)
+ return ..()
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 2931c52a5c..ab0b0d933a 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -113,7 +113,6 @@
to_chat(src, "Error: Use the admin IRC/Discord channel, nerd.", confidential = TRUE)
return
-
else
//get message text, limit it's length.and clean/escape html
if(!msg)
@@ -128,11 +127,16 @@
else
if(holder)
to_chat(src, "Error: Admin-PM: Client not found.", confidential = TRUE)
- to_chat(src, "Message not sent:
[msg]", confidential = TRUE)
+ to_chat(src, "Message not sent:
[sanitize(msg)]", confidential = TRUE)
if(recipient_ticket)
recipient_ticket.AddInteraction("No client found, message not sent:
[msg]")
return
else
+ //clean the message if it's not sent by a high-rank admin
+ if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
+ msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
+ if(!msg)
+ return
current_ticket.MessageNoRecipient(msg)
return
@@ -141,7 +145,7 @@
to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).", confidential = TRUE)
return
- if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
+ if(src.handle_spam_prevention(msg,MUTE_ADMINHELP))
return
//clean the message if it's not sent by a high-rank admin
diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm
index 6e188a6c7a..c9a5cafd9b 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -4,12 +4,10 @@
if(!check_rights(R_SOUNDS))
return
+ var/freq = 1
var/vol = input(usr, "What volume would you like the sound to play at?",, 100) as null|num
if(!vol)
return
- var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
- if(!freq)
- freq = 1
vol = clamp(vol, 1, 100)
var/sound/admin_sound = new()
@@ -18,14 +16,14 @@
admin_sound.channel = CHANNEL_ADMIN
admin_sound.frequency = freq
admin_sound.wait = 1
- admin_sound.repeat = 0
+ admin_sound.repeat = FALSE
admin_sound.status = SOUND_STREAM
admin_sound.volume = vol
var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel")
switch(res)
if("Yes")
- to_chat(world, "An admin played: [S]")
+ to_chat(world, "An admin played: [S]", confidential = TRUE)
if("Cancel")
return
@@ -49,7 +47,7 @@
log_admin("[key_name(src)] played a local sound [S]")
message_admins("[key_name_admin(src)] played a local sound [S]")
- playsound(get_turf(src.mob), S, 50, 0, 0)
+ playsound(get_turf(src.mob), S, 50, FALSE, FALSE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Local Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/play_web_sound()
diff --git a/code/modules/admin/view_variables/mass_edit_variables.dm b/code/modules/admin/view_variables/mass_edit_variables.dm
index a498868436..c45513e812 100644
--- a/code/modules/admin/view_variables/mass_edit_variables.dm
+++ b/code/modules/admin/view_variables/mass_edit_variables.dm
@@ -104,7 +104,7 @@
if (!thing)
continue
var/datum/D = thing
- if (D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE)
+ if (D.vv_edit_var(variable, initial(D.vars[variable]), TRUE) != FALSE)
accepted++
else
rejected++
@@ -135,7 +135,7 @@
for(var/V in varsvars)
new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]")
- if (D.vv_edit_var(variable, new_value) != FALSE)
+ if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
accepted++
else
rejected++
@@ -161,7 +161,7 @@
if(many && !new_value)
new_value = new type()
- if (D.vv_edit_var(variable, new_value) != FALSE)
+ if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
accepted++
else
rejected++
@@ -176,7 +176,7 @@
if (!thing)
continue
var/datum/D = thing
- if (D.vv_edit_var(variable, new_value) != FALSE)
+ if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
accepted++
else
rejected++
diff --git a/code/modules/admin/view_variables/reference_tracking.dm b/code/modules/admin/view_variables/reference_tracking.dm
index 70aac2f107..c293500298 100644
--- a/code/modules/admin/view_variables/reference_tracking.dm
+++ b/code/modules/admin/view_variables/reference_tracking.dm
@@ -109,7 +109,7 @@ GLOBAL_LIST_EMPTY(deletion_failures)
set name = "Find References"
set src in world
- find_references(FALSE)
+ find_references_legacy(FALSE)
/datum/proc/find_references_legacy(skip_alert)
@@ -164,7 +164,7 @@ GLOBAL_LIST_EMPTY(deletion_failures)
qdel(src, TRUE) //force a qdel
if(!running_find_references)
- find_references(TRUE)
+ find_references_legacy(TRUE)
/datum/verb/qdel_then_if_fail_find_references()
diff --git a/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm b/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm
index 8ac083f68b..c35016cf2e 100644
--- a/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm
@@ -9,4 +9,4 @@
/obj/item/organ/heart/gland/ventcrawling/activate()
to_chat(owner, "You feel very stretchy.")
- owner.ventcrawler = VENTCRAWLER_ALWAYS
+ owner.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
diff --git a/code/modules/antagonists/blob/blob/overmind.dm b/code/modules/antagonists/blob/blob/overmind.dm
index 02be432045..18f6817ec9 100644
--- a/code/modules/antagonists/blob/blob/overmind.dm
+++ b/code/modules/antagonists/blob/blob/overmind.dm
@@ -277,3 +277,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
var/datum/antagonist/blob/B = mind.has_antag_datum(/datum/antagonist/blob)
if(!B)
mind.add_antag_datum(/datum/antagonist/blob)
+
+//the same but it's forced to be allowed by default as cameras usually don't allow emoting
+/mob/camera/blob/emote(act, m_type=1, message = null, intentional = FALSE, forced = TRUE)
+ . = ..()
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
index 43163f6a70..2cb50c6b3b 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
@@ -13,8 +13,9 @@
var/nightime_duration = 900 //15 Minutes
/obj/effect/sunlight/Initialize()
- countdown()
- hud_tick()
+ . = ..()
+ INVOKE_ASYNC(src, .proc/countdown)
+ INVOKE_ASYNC(src, .proc/hud_tick)
/obj/effect/sunlight/proc/countdown()
set waitfor = FALSE
diff --git a/code/modules/antagonists/bloodsucker/datum_vassal.dm b/code/modules/antagonists/bloodsucker/datum_vassal.dm
index c065d5fe7c..91e89e71e6 100644
--- a/code/modules/antagonists/bloodsucker/datum_vassal.dm
+++ b/code/modules/antagonists/bloodsucker/datum_vassal.dm
@@ -90,7 +90,7 @@
/datum/antagonist/vassal/greet()
to_chat(owner, "You are now the mortal servant of [master.owner.current], a bloodsucking vampire!")
to_chat(owner, "The power of [master.owner.current.p_their()] immortal blood compells you to obey [master.owner.current.p_them()] in all things, even offering your own life to prolong theirs.
\
- You are not required to obey any other Bloodsucker, for only [master.owner.current] is your master. The laws of Nanotransen do not apply to you now; only your vampiric master's word must be obeyed.")
+ You are not required to obey any other Bloodsucker, for only [master.owner.current] is your master. The laws of Nanotrasen do not apply to you now; only your vampiric master's word must be obeyed.")
// Effects...
owner.current.playsound_local(null, 'sound/magic/mutate.ogg', 100, FALSE, pressure_affected = FALSE)
//owner.store_memory("You became the mortal servant of [master.owner.current], a bloodsucking vampire!")
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index dacd568993..7a34af4d13 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -213,7 +213,7 @@
if(canrespec)
to_chat(owner.current, "We have removed our evolutions from this form, and are now ready to readapt.")
reset_powers()
- playsound(get_turf(owner.current), 'sound/effects/lingreadapt.ogg', 75, TRUE, 5, soundenvwet = 0)
+ playsound(get_turf(owner.current), 'sound/effects/lingreadapt.ogg', 75, TRUE, 5)
canrespec = 0
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, "Readapt")
return 1
diff --git a/code/modules/antagonists/changeling/powers/shriek.dm b/code/modules/antagonists/changeling/powers/shriek.dm
index 0e2515fdae..dae1da9f8f 100644
--- a/code/modules/antagonists/changeling/powers/shriek.dm
+++ b/code/modules/antagonists/changeling/powers/shriek.dm
@@ -29,7 +29,7 @@
for(var/obj/machinery/light/L in range(4, user))
L.on = 1
L.break_light_tube()
- playsound(get_turf(user), 'sound/effects/lingscreech.ogg', 75, TRUE, 5, soundenvwet = 0)
+ playsound(get_turf(user), 'sound/effects/lingscreech.ogg', 75, TRUE, 5)
return TRUE
/obj/effect/proc_holder/changeling/dissonant_shriek
@@ -49,5 +49,5 @@
L.on = 1
L.break_light_tube()
empulse_using_range(get_turf(user), 8, TRUE)
- playsound(get_turf(user), 'sound/effects/lingempscreech.ogg', 75, TRUE, 5, soundenvwet = 0)
+ playsound(get_turf(user), 'sound/effects/lingempscreech.ogg', 75, TRUE, 5)
return TRUE
diff --git a/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm b/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm
index 3ea4668df8..1da49efe3c 100644
--- a/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm
+++ b/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm
@@ -22,7 +22,7 @@
for(var/mob/M in GLOB.player_list)
if(M.z == z)
if(get_dist(src, M) >= 7)
- M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE, falloff = 10)
+ M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE)
else
M.playsound_local(src, 'sound/magic/blink.ogg', 50, FALSE)
diff --git a/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm b/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm
index 59a86ffa34..58ee73ef5b 100644
--- a/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm
+++ b/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm
@@ -165,14 +165,16 @@
/datum/clockwork_rite/treat_wounds/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
if(!target)
return FALSE
- if(!target.all_wounds.len)
+ if(!target.all_wounds || !target.all_wounds.len)
to_chat(invoker, "This one does not require mending.")
return FALSE
.= ..()
if(!.)
return FALSE
target.adjustToxLoss(10 * target.all_wounds.len)
- QDEL_LIST(target.all_wounds)
+ for(var/i in target.all_wounds)
+ var/datum/wound/mended = i
+ mended.remove_wound()
to_chat(target, "You feel your wounds heal, but are overcome with deep nausea.")
new /obj/effect/temp_visual/ratvar/sigil/vitality(T)
diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_shield.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_shield.dm
index a681e9b38b..fb7b4f8a94 100644
--- a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_shield.dm
+++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_shield.dm
@@ -22,7 +22,7 @@
. = ..()
desc = initial(desc)
-obj/item/shield/riot/ratvarian/proc/calc_bash_mult()
+/obj/item/shield/riot/ratvarian/proc/calc_bash_mult()
var/bash_mult = 0
if(!dam_absorbed)
return 1
diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
index 571a817024..ff6dc12ce1 100644
--- a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
+++ b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
@@ -203,10 +203,10 @@
to_chat(user, "You need to hold the slab in your active hand to recite scripture!")
return FALSE
var/initial_tier = initial(scripture.tier)
- if(initial_tier == SCRIPTURE_PERIPHERAL)
+ if(initial_tier == SCRIPTURE_PERIPHERAL && !issilicon(user)) //Silicons use peripheral scripture & cannot open the slab.
to_chat(user, "Nice try using href exploits")
return
- if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
+ if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier] &&!issilicon(user)) //silicons can't choose their spells, so lets allow them to always cast their assigned ones.
to_chat(user, "That scripture is not unlocked, and cannot be recited!")
return FALSE
var/datum/clockwork_scripture/scripture_to_recite = new scripture
diff --git a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm
index 7478d45b08..3b507fb57c 100644
--- a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm
@@ -149,7 +149,7 @@
transform = matrix() * 2
animate(src, transform = matrix() * 0.5, time = 30, flags = ANIMATION_END_NOW)
-obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
+/obj/structure/destructible/clockwork/massive/celestial_gateway/Destroy()
STOP_PROCESSING(SSprocessing, src)
if(!purpose_fulfilled)
var/area/gate_area = get_area(src)
diff --git a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor.dm b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor.dm
index af29bc9b31..c5d1bc2ded 100644
--- a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor.dm
@@ -15,3 +15,5 @@
audible_message("*click*")
playsound(src, 'sound/items/screwdriver2.ogg', 50, TRUE)
activate()
+
+ . = ..()
diff --git a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor_mech.dm b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor_mech.dm
index 10a5b7787f..21735ad6d7 100644
--- a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor_mech.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/pressure_sensor_mech.dm
@@ -8,7 +8,7 @@
alpha = 75
/obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/mech/Crossed(atom/movable/AM)
-
+ . = ..()
if(!istype(AM,/obj/mecha/))
return
diff --git a/code/modules/antagonists/clockcult/clock_structures/traps/steam_vent.dm b/code/modules/antagonists/clockcult/clock_structures/traps/steam_vent.dm
index 6aede1592e..7d6eea1c79 100644
--- a/code/modules/antagonists/clockcult/clock_structures/traps/steam_vent.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/traps/steam_vent.dm
@@ -21,3 +21,5 @@
if(isliving(AM) && opacity)
var/mob/living/L = AM
L.adjust_fire_stacks(-1) //It's wet!
+ return
+ . = ..()
diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm
index 293b1de3dc..069080170e 100644
--- a/code/modules/antagonists/devil/true_devil/_true_devil.dm
+++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm
@@ -11,7 +11,6 @@
gender = NEUTER
health = 350
maxHealth = 350
- ventcrawler = VENTCRAWLER_NONE
density = TRUE
pass_flags = 0
sight = (SEE_TURFS | SEE_OBJS)
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
index ea226a3cb6..1e43754477 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
@@ -10,6 +10,8 @@
var/give_equipment = TRUE
var/list/researched_knowledge = list()
var/total_sacrifices = 0
+ var/list/sac_targetted = list() //Which targets did living hearts give them, but they did not sac?
+ var/list/actually_sacced = list() //Which targets did they actually sac?
var/ascended = FALSE
/datum/antagonist/heretic/admin_add(datum/mind/new_owner,mob/admin)
@@ -175,6 +177,17 @@
knowledge_message += "[EK.name]"
parts += knowledge_message.Join(", ")
+ parts += "Targets assigned by living hearts, but not sacrificed:"
+ if(!sac_targetted.len)
+ parts += "None."
+ else
+ parts += sac_targetted.Join(",")
+ parts += "Sacrifices performed:"
+ if(!actually_sacced.len)
+ parts += "None!"
+ else
+ parts += actually_sacced.Join(",")
+
return parts.Join("
")
////////////////
// Knowledge //
@@ -213,6 +226,23 @@
if(ascended)
. += 20
+/datum/antagonist/heretic/antag_panel()
+ var/list/parts = list()
+ parts += ..()
+ parts += "Targets currently assigned by living hearts (Can give a false negative if they stole someone elses living heart):"
+ if(!sac_targetted.len)
+ parts += "None."
+ else
+ parts += sac_targetted.Join(",")
+ parts += "Targets actually sacrificed:"
+ if(!actually_sacced.len)
+ parts += "None."
+ else
+ parts += actually_sacced.Join(",")
+
+ return (parts.Join("
") + "
")
+
+
////////////////
// Objectives //
////////////////
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm
index da2c61ad16..fbf0740e50 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_items.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm
@@ -6,6 +6,17 @@
w_class = WEIGHT_CLASS_SMALL
///Target
var/mob/living/carbon/human/target
+ var/datum/antagonist/heretic/sac_targetter //The heretic who used this to acquire the current target - gets cleared when target gets sacrificed.
+
+/obj/item/living_heart/Initialize()
+ . = ..()
+ GLOB.living_heart_cache.Add(src) //Add is better than +=.
+
+/obj/item/living_heart/Destroy()
+ GLOB.living_heart_cache.Remove(src)
+ if(sac_targetter && target)
+ sac_targetter.sac_targetted.Remove(target.real_name)
+ return ..()
/obj/item/living_heart/attack_self(mob/user)
. = ..()
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
index e0189944e5..481ac08ea5 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
@@ -1,6 +1,6 @@
/**
- * #Eldritch Knwoledge
+ * #Eldritch Knowledge
*
* Datum that makes eldritch cultist interesting.
*
@@ -252,6 +252,10 @@
LH.target = null
var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
+ EC.actually_sacced.Add(H.real_name)
+ if(LH.sac_targetter)
+ LH.sac_targetter.sac_targetted.Remove(H.real_name)
+ LH.sac_targetter = null
EC.total_sacrifices++
for(var/X in carbon_user.get_all_gear())
if(!istype(X,/obj/item/forbidden_book))
@@ -265,8 +269,14 @@
var/datum/objective/A = new
A.owner = user.mind
var/list/targets = list()
+ var/list/target_blacklist = list()
+ for(var/obj/item/living_heart/CLH in GLOB.living_heart_cache)
+ if(!CLH || !CLH.target || !CLH.target.mind)
+ continue
+ target_blacklist.Add(CLH.target.mind)
+
for(var/i in 0 to 3)
- var/datum/mind/targeted = A.find_target()//easy way, i dont feel like copy pasting that entire block of code
+ var/datum/mind/targeted = A.find_target(blacklist = target_blacklist)//easy way, i dont feel like copy pasting that entire block of code
if(!targeted)
break
targets[targeted.current.real_name] = targeted.current
@@ -274,9 +284,24 @@
if(!LH.target && targets.len)
LH.target = pick(targets) //Tsk tsk, you can and will get another target if you want it or not.
+
+ if(LH.target)
+ target_blacklist = list()
+ for(var/obj/item/living_heart/CLH in (GLOB.living_heart_cache - LH)) //Recreate blacklist, excluding ourselves.
+ if(!CLH || !CLH.target || !CLH.target.mind)
+ continue
+ target_blacklist.Add(CLH.target.mind)
+ if(LH.target.mind in target_blacklist) //Someone was faster, or you tried to cheese the system.
+ to_chat(user, "It seems you were too slow, and your target of choice has already been selected by another living heart!")
+ LH.target = null
+
qdel(A)
if(LH.target)
to_chat(user,"Your new target has been selected, go and sacrifice [LH.target.real_name]!")
+ var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
+ LH.sac_targetter = EC
+ EC.sac_targetted.Add(LH.target.real_name)
+
else
to_chat(user,"target could not be found for living heart.")
diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm
index 5844aad02b..077c380ef0 100644
--- a/code/modules/antagonists/morph/morph.dm
+++ b/code/modules/antagonists/morph/morph.dm
@@ -15,7 +15,6 @@
stop_automated_movement = 1
status_flags = CANPUSH
pass_flags = PASSTABLE
- ventcrawler = VENTCRAWLER_ALWAYS
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxHealth = 150
@@ -53,6 +52,10 @@
You can attack any item or dead creature to consume it - creatures will fully restore your health. \
Finally, you can restore yourself to your original form while morphed by shift-clicking yourself."
+/mob/living/simple_animal/hostile/morph/Initialize()
+ . = ..()
+ src.AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
+
/mob/living/simple_animal/hostile/morph/examine(mob/user)
if(morphed)
. = form.examine(user)
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index f5ebcffe35..ef58e5af39 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -72,6 +72,7 @@
var/list/drained_mobs = list() //Cannot harvest the same mob twice
var/perfectsouls = 0 //How many perfect, regen-cap increasing souls the revenant has. //TODO, add objective for getting a perfect soul(s?)
var/generated_objectives_and_spells = FALSE
+ var/telekinesis_cooldown
/mob/living/simple_animal/revenant/Initialize(mapload)
. = ..()
@@ -93,13 +94,16 @@
/mob/living/simple_animal/revenant/Login()
..()
- to_chat(src, "You are a revenant.")
- to_chat(src, "Your formerly mundane spirit has been infused with alien energies and empowered into a revenant.")
- to_chat(src, "You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds.")
- to_chat(src, "You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.")
- to_chat(src, "To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.")
- to_chat(src, "You do not remember anything of your past lives, nor will you remember anything about this one after your death.")
- to_chat(src, "Be sure to read the wiki page to learn more.")
+ var/revenant_greet
+ revenant_greet += "You are a revenant."
+ revenant_greet += "Your formerly mundane spirit has been infused with alien energies and empowered into a revenant."
+ revenant_greet += "You are not dead, not alive, but somewhere in between. You are capable of limited interaction with both worlds."
+ revenant_greet += "You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable."
+ revenant_greet += "To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities."
+ revenant_greet += "You do not remember anything of your past lives, nor will you remember anything about this one after your death."
+ revenant_greet += "Be sure to read the wiki page to learn more."
+ revenant_greet += "You are also able to telekinetically throw objects by clickdragging them."
+ to_chat(src, revenant_greet)
if(!generated_objectives_and_spells)
generated_objectives_and_spells = TRUE
mind.assigned_role = ROLE_REVENANT
@@ -317,6 +321,12 @@
to_chat(src, "Lost [essence_amt]E[source ? " from [source]":""].")
return 1
+/mob/living/simple_animal/revenant/proc/telekinesis_cooldown_end()
+ if(!telekinesis_cooldown)
+ CRASH("telekinesis_cooldown_end ran when telekinesis_cooldown on [src] was false")
+ else
+ telekinesis_cooldown = FALSE
+
/mob/living/simple_animal/revenant/proc/death_reset()
revealed = FALSE
unreveal_time = 0
@@ -431,6 +441,38 @@
qdel(revenant)
..()
+/proc/RevenantThrow(over, mob/user, obj/item/throwable)
+ var/mob/living/simple_animal/revenant/spooker = user
+ if(!istype(throwable))
+ return
+ if(!throwable.anchored && !spooker.telekinesis_cooldown && spooker.essence > 20)
+ if(7 < get_dist(throwable, spooker))
+ return
+ if(3 >= get_dist(throwable, spooker))
+ spooker.stun(10)
+ spooker.reveal(25)
+ else
+ spooker.stun(20)
+ spooker.reveal(50)
+ spooker.change_essence_amount(-20, FALSE, "telekinesis")
+ spooker.telekinesis_cooldown = TRUE
+ throwable.float(TRUE, TRUE)
+ sleep(20)
+ throwable.DoRevenantThrowEffects(over)
+ throwable.throw_at(over, 10, 2)
+ ADD_TRAIT(throwable, TRAIT_SPOOKY_THROW, "revenant")
+ log_combat(throwable, over, "spooky telekinesised at", throwable)
+ var/obj/effect/temp_visual/telekinesis/T = new(get_turf(throwable))
+ T.color = "#8715b4"
+ addtimer(CALLBACK(spooker, /mob/living/simple_animal/revenant.proc/telekinesis_cooldown_end), 50)
+ sleep(5)
+ throwable.float(FALSE, TRUE)
+
+
+//Use this for effects you want to happen when a revenant throws stuff, check the TRAIT_SPOOKY_THROW if you want to know if its still being thrown
+/obj/item/proc/DoRevenantThrowEffects(atom/target)
+ return TRUE
+
//objectives
/datum/objective/revenant
var/targetAmount = 100
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index 7444905b89..d1bdac7e05 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -93,7 +93,6 @@
AIStatus = AI_OFF
pass_flags = PASSTABLE
mob_size = MOB_SIZE_TINY
- ventcrawler = VENTCRAWLER_ALWAYS
ranged = 1
projectiletype = /obj/item/projectile/beam/disabler
ranged_cooldown_time = 20
@@ -112,6 +111,7 @@
remove_verb(src, /mob/living/verb/pulled)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
+ AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
/mob/living/simple_animal/hostile/swarmer/med_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm
index 28298f4983..525f40d1f7 100644
--- a/code/modules/antagonists/traitor/equipment/contractor.dm
+++ b/code/modules/antagonists/traitor/equipment/contractor.dm
@@ -43,7 +43,7 @@
var/list/to_generate = list(
CONTRACT_PAYOUT_LARGE,
CONTRACT_PAYOUT_MEDIUM,
- CONTRACT_PAYOUT_SMALL,
+ CONTRACT_PAYOUT_MEDIUM,
CONTRACT_PAYOUT_SMALL,
CONTRACT_PAYOUT_SMALL,
CONTRACT_PAYOUT_SMALL
diff --git a/code/modules/arousal/toys/dildos.dm b/code/modules/arousal/toys/dildos.dm
index 0cfa57e5fc..2482b93300 100644
--- a/code/modules/arousal/toys/dildos.dm
+++ b/code/modules/arousal/toys/dildos.dm
@@ -103,29 +103,29 @@
name = "knotted dildo"
attack_verb = list("penetrated", "knotted", "slapped", "inseminated")
-obj/item/dildo/human
+/obj/item/dildo/human
dildo_shape = "human"
name = "human dildo"
attack_verb = list("penetrated", "slapped", "inseminated")
-obj/item/dildo/plain
+/obj/item/dildo/plain
dildo_shape = "plain"
name = "plain dildo"
attack_verb = list("penetrated", "slapped", "inseminated")
-obj/item/dildo/flared
+/obj/item/dildo/flared
dildo_shape = "flared"
name = "flared dildo"
attack_verb = list("penetrated", "slapped", "neighed", "gaped", "prolapsed", "inseminated")
-obj/item/dildo/flared/huge
+/obj/item/dildo/flared/huge
name = "literal horse cock"
desc = "THIS THING IS HUGE!"
dildo_size = 4
force = 10
hitsound = 'sound/weapons/klonk.ogg'
-obj/item/dildo/custom
+/obj/item/dildo/custom
name = "customizable dildo"
desc = "Thanks to significant advances in synthetic nanomaterials, this dildo is capable of taking on many different forms to fit the user's preferences! Pricy!"
can_customize = TRUE
@@ -136,9 +136,9 @@ obj/item/dildo/custom
// Suicide acts, by request
/obj/item/dildo/proc/manual_suicide(mob/living/user)
- user.visible_message("[user] finally finishes deepthroating the [src], and their life.")
- user.adjustOxyLoss(200)
- user.death(0)
+ user.visible_message("[user] finally finishes deepthroating the [src], and their life.")
+ user.adjustOxyLoss(200)
+ user.death(0)
/obj/item/dildo/suicide_act(mob/living/user)
// is_knotted = ((src.dildo_shape == "knotted")?"They swallowed the knot":"Their face is turning blue")
diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm
index 8a0645f311..07a9f499f8 100644
--- a/code/modules/assembly/flash.dm
+++ b/code/modules/assembly/flash.dm
@@ -30,6 +30,9 @@
attack(user,user)
return FIRELOSS
+/obj/item/assembly/flash/DoRevenantThrowEffects(atom/target)
+ AOE_flash()
+
/obj/item/assembly/flash/update_icon(flash = FALSE)
cut_overlays()
attached_overlays = list()
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index bf56449a0c..84e70bbbb3 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -62,6 +62,7 @@
master.update_icon()
/obj/item/assembly_holder/Crossed(atom/movable/AM as mob|obj)
+ . = ..()
if(a_left)
a_left.Crossed(AM)
if(a_right)
diff --git a/code/modules/asset_cache/asset_cache_client.dm b/code/modules/asset_cache/asset_cache_client.dm
index f462fe386b..3cff8cb41f 100644
--- a/code/modules/asset_cache/asset_cache_client.dm
+++ b/code/modules/asset_cache/asset_cache_client.dm
@@ -1,5 +1,5 @@
-/// Process asset cache client topic calls for "asset_cache_confirm_arrival=[INT]"
+/// Process asset cache client topic calls for `"asset_cache_confirm_arrival=[INT]"`
/client/proc/asset_cache_confirm_arrival(job_id)
var/asset_cache_job = round(text2num(job_id))
//because we skip the limiter, we have to make sure this is a valid arrival and not somebody tricking us into letting them append to a list without limit.
@@ -10,7 +10,7 @@
return asset_cache_job || TRUE
-/// Process asset cache client topic calls for "asset_cache_preload_data=[HTML+JSON_STRING]
+/// Process asset cache client topic calls for `"asset_cache_preload_data=[HTML+JSON_STRING]"`
/client/proc/asset_cache_preload_data(data)
var/json = data
var/list/preloaded_assets = json_decode(json)
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 3b6a30a02a..22fb266033 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -47,8 +47,9 @@
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
- "smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
- "borg_mon.gif" = 'icons/program_icons/borg_mon.gif'
+ "smmon_6.gif" = 'icons/program_icons/smmon_6.gif'
+ // "borg_mon.gif" = 'icons/program_icons/borg_mon.gif',
+ // "robotact.gif" = 'icons/program_icons/robotact.gif'
)
/datum/asset/simple/radar_assets
@@ -156,7 +157,6 @@
)
/datum/asset/simple/namespaced/fontawesome
- legacy = TRUE
assets = list(
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
@@ -248,6 +248,10 @@
"rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
"snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
"ascension" = 'icons/UI_Icons/Achievements/Misc/ascension.png',
+ "ashascend" = 'icons/UI_Icons/Achievements/Misc/ashascend.png',
+ "fleshascend" = 'icons/UI_Icons/Achievements/Misc/fleshascend.png',
+ "rustascend" = 'icons/UI_Icons/Achievements/Misc/rustascend.png',
+ "voidascend" = 'icons/UI_Icons/Achievements/Misc/voidascend.png',
"mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
"assistant" = 'icons/UI_Icons/Achievements/Mafia/assistant.png',
"changeling" = 'icons/UI_Icons/Achievements/Mafia/changeling.png',
@@ -288,7 +292,7 @@
)
/datum/asset/spritesheet/simple/pills
- name ="pills"
+ name = "pills"
assets = list(
"pill1" = 'icons/UI_Icons/Pills/pill1.png',
"pill2" = 'icons/UI_Icons/Pills/pill2.png',
@@ -313,7 +317,28 @@
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
)
-
+/*
+/datum/asset/spritesheet/simple/condiments
+ name = "condiments"
+ assets = list(
+ CONDIMASTER_STYLE_FALLBACK = 'icons/UI_Icons/Condiments/emptycondiment.png',
+ "enzyme" = 'icons/UI_Icons/Condiments/enzyme.png',
+ "flour" = 'icons/UI_Icons/Condiments/flour.png',
+ "mayonnaise" = 'icons/UI_Icons/Condiments/mayonnaise.png',
+ "milk" = 'icons/UI_Icons/Condiments/milk.png',
+ "blackpepper" = 'icons/UI_Icons/Condiments/peppermillsmall.png',
+ "rice" = 'icons/UI_Icons/Condiments/rice.png',
+ "sodiumchloride" = 'icons/UI_Icons/Condiments/saltshakersmall.png',
+ "soymilk" = 'icons/UI_Icons/Condiments/soymilk.png',
+ "soysauce" = 'icons/UI_Icons/Condiments/soysauce.png',
+ "sugar" = 'icons/UI_Icons/Condiments/sugar.png',
+ "ketchup" = 'icons/UI_Icons/Condiments/ketchup.png',
+ "capsaicin" = 'icons/UI_Icons/Condiments/hotsauce.png',
+ "frostoil" = 'icons/UI_Icons/Condiments/coldsauce.png',
+ "bbqsauce" = 'icons/UI_Icons/Condiments/bbqsauce.png',
+ "cornoil" = 'icons/UI_Icons/Condiments/oliveoil.png',
+ )
+*/
//this exists purely to avoid meta by pre-loading all language icons.
/datum/asset/language/register()
for(var/path in typesof(/datum/language))
@@ -484,3 +509,31 @@
/datum/asset/spritesheet/mafia/register()
InsertAll("", 'icons/obj/mafia.dmi')
..()
+
+/datum/asset/simple/portraits
+ var/tab = "use subtypes of this please"
+ assets = list()
+
+/datum/asset/simple/portraits/New()
+ if(!SSpersistence.paintings || !SSpersistence.paintings[tab] || !length(SSpersistence.paintings[tab]))
+ return
+ for(var/p in SSpersistence.paintings[tab])
+ var/list/portrait = p
+ var/png = "data/paintings/[tab]/[portrait["md5"]].png"
+ if(fexists(png))
+ assets[portrait["title"]] = png
+ ..() //this is where it registers all these assets we added to the list
+
+/datum/asset/simple/portraits/library
+ tab = "library"
+
+/datum/asset/simple/portraits/library_secure
+ tab = "library_secure"
+
+/datum/asset/simple/portraits/library_private
+ tab = "library_private"
+
+/datum/asset/simple/safe
+ assets = list(
+ "safe_dial.png" = 'html/safe_dial.png'
+ )
diff --git a/code/modules/asset_cache/readme.md b/code/modules/asset_cache/readme.md
index 82e6bea896..c8c9d78b71 100644
--- a/code/modules/asset_cache/readme.md
+++ b/code/modules/asset_cache/readme.md
@@ -24,7 +24,7 @@ Call .get_url_mappings() to get an associated list with the urls your assets can
See the documentation for `/datum/asset_transport` for the backend api the asset datums utilize.
-The global variable `SSassets.transport` contains the currently configured transport.
+The global variable `SSassets.transport` contains the currently configured transport.
@@ -32,6 +32,6 @@ The global variable `SSassets.transport` contains the currently configured trans
Because byond browse() calls use non-blocking queues, if your code uses output() (which bypasses all of these queues) to invoke javascript functions you will need to first have the javascript announce to the server it has loaded before trying to invoke js functions.
-To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
+To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
CSS files that use url() can be made to use the CDN without needing to rewrite all url() calls in code by using the namespaced helper datum. See the documentation for `/datum/asset/simple/namespaced` for details.
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index eee8859485..244b51bd2b 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -49,6 +49,7 @@
SSair.add_to_active(T)
return ..()
+/// Function for Extools Atmos
/turf/proc/update_air_ref()
/////////////////GAS MIXTURE PROCS///////////////////
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index 25e4084524..7f4a8cd794 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -44,7 +44,7 @@
. = ..()
if(is_type_in_list(src, GLOB.ventcrawl_machinery) && isliving(user))
var/mob/living/L = user
- if(L.ventcrawler)
+ if(SEND_SIGNAL(L, COMSIG_CHECK_VENTCRAWL))
. += "Alt-click to crawl through it."
/obj/machinery/atmospherics/New(loc, process = TRUE, setdir)
@@ -318,7 +318,7 @@
/obj/machinery/atmospherics/AltClick(mob/living/L)
if(is_type_in_typecache(src, GLOB.ventcrawl_machinery))
- return L.handle_ventcrawl(src)
+ return SEND_SIGNAL(L, COMSIG_HANDLE_VENTCRAWL, src)
return ..()
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index 0eebf22be4..2690ad555b 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -261,7 +261,7 @@
new /obj/item/stack/sheet/metal (loc, 5)
qdel(src)
-obj/machinery/portable_atmospherics/canister/welder_act(mob/living/user, obj/item/I)
+/obj/machinery/portable_atmospherics/canister/welder_act(mob/living/user, obj/item/I)
..()
if(user.a_intent == INTENT_HARM)
return FALSE
diff --git a/code/modules/atmospherics/multiz.dm b/code/modules/atmospherics/multiz.dm
index 79a5c2cf14..cb52d03ff6 100644
--- a/code/modules/atmospherics/multiz.dm
+++ b/code/modules/atmospherics/multiz.dm
@@ -1,4 +1,5 @@
-obj/machinery/atmospherics/pipe/simple/multiz ///This is an atmospherics pipe which can relay air up a deck (Z+1). It currently only supports being on pipe layer 1
+/// This is an atmospherics pipe which can relay air up/down a deck.
+/obj/machinery/atmospherics/pipe/simple/multiz
name = "multi deck pipe adapter"
desc = "An adapter which allows pipes to connect to other pipenets on different decks."
icon_state = "multiz_pipe"
@@ -24,6 +25,4 @@ obj/machinery/atmospherics/pipe/simple/multiz ///This is an atmospherics pipe wh
if(above)
nodes += above
above.nodes += src //Two way travel :)
- return ..()
- else
- return ..()
+ return ..()
diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm
index 3426208fae..d32905e007 100644
--- a/code/modules/awaymissions/capture_the_flag.dm
+++ b/code/modules/awaymissions/capture_the_flag.dm
@@ -7,6 +7,8 @@
#define AMMO_DROP_LIFETIME 300
#define CTF_REQUIRED_PLAYERS 4
+
+
/obj/item/ctf
name = "banner"
icon = 'icons/obj/items_and_weapons.dmi'
@@ -16,13 +18,13 @@
righthand_file = 'icons/mob/inhands/equipment/banners_righthand.dmi'
desc = "A banner with Nanotrasen's logo on it."
slowdown = 2
- item_flags = SLOWS_WHILE_IN_HAND
throw_speed = 0
throw_range = 1
force = 200
armour_penetration = 1000
resistance_flags = INDESTRUCTIBLE
anchored = TRUE
+ item_flags = SLOWS_WHILE_IN_HAND
var/team = WHITE_TEAM
var/reset_cooldown = 0
var/anyonecanpickup = TRUE
@@ -53,12 +55,13 @@
to_chat(M, "\The [src] has been returned to base!")
STOP_PROCESSING(SSobj, src)
-/obj/item/ctf/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
+//ATTACK HAND IGNORING PARENT RETURN VALUE
+/obj/item/ctf/on_attack_hand(mob/living/user)
if(!is_ctf_target(user) && !anyonecanpickup)
- to_chat(user, "Non players shouldn't be moving the flag!")
+ to_chat(user, "Non-players shouldn't be moving the flag!")
return
if(team in user.faction)
- to_chat(user, "You can't move your own flag!")
+ to_chat(user, "You can't move your own flag!")
return
if(loc == user)
if(!user.dropItemToGround(src))
@@ -68,7 +71,7 @@
if(!user.put_in_active_hand(src))
dropped(user)
return
- user.anchored = TRUE
+ user.set_anchored(TRUE)
user.status_flags &= ~CANPUSH
for(var/mob/M in GLOB.player_list)
var/area/mob_area = get_area(M)
@@ -79,7 +82,7 @@
/obj/item/ctf/dropped(mob/user)
..()
- user.anchored = FALSE
+ user.set_anchored(FALSE)
user.status_flags |= CANPUSH
reset_cooldown = world.time + 200 //20 seconds
START_PROCESSING(SSobj, src)
@@ -172,20 +175,20 @@
GLOB.poi_list.Remove(src)
..()
-/obj/machinery/capture_the_flag/process()
+/obj/machinery/capture_the_flag/process(delta_time)
for(var/i in spawned_mobs)
if(!i)
spawned_mobs -= i
continue
// Anyone in crit, automatically reap
- var/mob/living/M = i
- if(M.InCritical() || M.stat == DEAD)
- ctf_dust_old(M)
+ var/mob/living/living_participant = i
+ if(living_participant.InCritical() || living_participant.stat == DEAD)
+ ctf_dust_old(living_participant)
else
// The changes that you've been hit with no shield but not
// instantly critted are low, but have some healing.
- M.adjustBruteLoss(-5)
- M.adjustFireLoss(-5)
+ living_participant.adjustBruteLoss(-2.5 * delta_time)
+ living_participant.adjustFireLoss(-2.5 * delta_time)
/obj/machinery/capture_the_flag/red
name = "Red CTF Controller"
@@ -212,6 +215,10 @@
toggle_all_ctf(user)
return
+
+ // if(!(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
+ // to_chat(user, "CTF has been temporarily disabled by admins.")
+ // return
people_who_want_to_play |= user.ckey
var/num = people_who_want_to_play.len
var/remaining = CTF_REQUIRED_PLAYERS - num
@@ -227,7 +234,7 @@
return
if(user.ckey in team_members)
if(user.ckey in recently_dead_ckeys)
- to_chat(user, "It must be more than [DisplayTimeText(respawn_cooldown)] from your last death to respawn!")
+ to_chat(user, "It must be more than [DisplayTimeText(respawn_cooldown)] from your last death to respawn!")
return
var/client/new_team_member = user.client
if(user.mind && user.mind.current)
@@ -239,10 +246,10 @@
if(CTF == src || CTF.ctf_enabled == FALSE)
continue
if(user.ckey in CTF.team_members)
- to_chat(user, "No switching teams while the round is going!")
+ to_chat(user, "No switching teams while the round is going!")
return
if(CTF.team_members.len < src.team_members.len)
- to_chat(user, "[src.team] has more team members than [CTF.team]. Try joining [CTF.team] team to even things up.")
+ to_chat(user, "[src.team] has more team members than [CTF.team]! Try joining [CTF.team] team to even things up.")
return
team_members |= user.ckey
var/client/new_team_member = user.client
@@ -258,7 +265,7 @@
addtimer(CALLBACK(src, .proc/clear_cooldown, body.ckey), respawn_cooldown, TIMER_UNIQUE)
body.dust()
-/obj/machinery/capture_the_flag/proc/clear_cooldown(var/ckey)
+/obj/machinery/capture_the_flag/proc/clear_cooldown(ckey)
recently_dead_ckeys -= ckey
/obj/machinery/capture_the_flag/proc/spawn_team_member(client/new_team_member)
@@ -270,7 +277,7 @@
M.equipOutfit(ctf_gear)
M.dna.species.punchdamagehigh = 25
M.dna.species.punchdamagelow = 25
- M.AddElement(/datum/element/ghost_role_eligibility)
+ M.AddElement(/datum/element/ghost_role_eligibility) //??
spawned_mobs += M
/obj/machinery/capture_the_flag/Topic(href, href_list)
@@ -293,14 +300,15 @@
victory()
/obj/machinery/capture_the_flag/proc/victory()
- for(var/mob/M in GLOB.mob_list)
- var/area/mob_area = get_area(M)
+ for(var/mob/_competitor in GLOB.mob_living_list)
+ var/mob/living/competitor = _competitor
+ var/area/mob_area = get_area(competitor)
if(istype(mob_area, /area/ctf))
- to_chat(M, "[team] team wins!")
- to_chat(M, "Teams have been cleared. Click on the machines to vote to begin another round.")
- for(var/obj/item/ctf/W in M)
- M.dropItemToGround(W)
- M.dust()
+ to_chat(competitor, "[team] team wins!")
+ to_chat(competitor, "Teams have been cleared. Click on the machines to vote to begin another round.")
+ for(var/obj/item/ctf/W in competitor)
+ competitor.dropItemToGround(W)
+ competitor.dust()
for(var/obj/machinery/control_point/control in GLOB.machines)
control.icon_state = "dominator"
control.controlling = null
@@ -328,7 +336,7 @@
dead_barricades.Cut()
- notify_ghosts("[name] has been activated!", enter_link="(Click to join the [team] team!) or click on the controller directly!", source = src, action=NOTIFY_ATTACK)
+ notify_ghosts("[name] has been activated!", enter_link="(Click to join the [team] team!) or click on the controller directly!", source = src, action=NOTIFY_ATTACK, header = "CTF has been activated")
if(!arena_reset)
reset_the_arena()
@@ -355,10 +363,10 @@
ctf_enabled = FALSE
arena_reset = FALSE
var/area/A = get_area(src)
- for(var/i in GLOB.mob_list)
- var/mob/M = i
- if((get_area(A) == A) && (M.ckey in team_members))
- M.dust()
+ for(var/_competitor in GLOB.mob_living_list)
+ var/mob/living/competitor = _competitor
+ if((get_area(A) == A) && (competitor.ckey in team_members))
+ competitor.dust()
team_members.Cut()
spawned_mobs.Cut()
recently_dead_ckeys.Cut()
@@ -375,18 +383,18 @@
CTF.ctf_gear = initial(ctf_gear)
CTF.respawn_cooldown = DEFAULT_RESPAWN
-/proc/ctf_floor_vanish(atom/target)
- if(isturf(target.loc))
- qdel(target)
-
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf
desc = "This looks like it could really hurt in melee."
force = 75
mag_type = /obj/item/ammo_box/magazine/m50/ctf
-/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped(mob/user)
+/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped()
. = ..()
- addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
+ addtimer(CALLBACK(src, .proc/floor_vanish), 1)
+
+/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/proc/floor_vanish()
+ if(isturf(loc))
+ qdel(src)
/obj/item/ammo_box/magazine/m50/ctf
ammo_type = /obj/item/ammo_casing/a50/ctf
@@ -400,6 +408,7 @@
/obj/item/projectile/bullet/ctf/prehit(atom/target)
if(is_ctf_target(target))
damage = 60
+ return //PROJECTILE_PIERCE_NONE /// hey uhh don't hit anyone behind them
. = ..()
/obj/item/gun/ballistic/automatic/laser/ctf
@@ -407,16 +416,24 @@
desc = "This looks like it could really hurt in melee."
force = 50
-/obj/item/gun/ballistic/automatic/laser/ctf/dropped(mob/user)
+/obj/item/gun/ballistic/automatic/laser/ctf/dropped()
. = ..()
- addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
+ addtimer(CALLBACK(src, .proc/floor_vanish), 1)
+
+/obj/item/gun/ballistic/automatic/laser/ctf/proc/floor_vanish()
+ if(isturf(loc))
+ qdel(src)
/obj/item/ammo_box/magazine/recharge/ctf
ammo_type = /obj/item/ammo_casing/caseless/laser/ctf
-/obj/item/ammo_box/magazine/recharge/ctf/dropped(mob/user)
+/obj/item/ammo_box/magazine/recharge/ctf/dropped()
. = ..()
- addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
+ addtimer(CALLBACK(src, .proc/floor_vanish), 1)
+
+/obj/item/ammo_box/magazine/recharge/ctf/proc/floor_vanish()
+ if(isturf(loc))
+ qdel(src)
/obj/item/ammo_casing/caseless/laser/ctf
projectile_type = /obj/item/projectile/beam/ctf
@@ -428,15 +445,16 @@
/obj/item/projectile/beam/ctf/prehit(atom/target)
if(is_ctf_target(target))
damage = 150
+ return //PROJECTILE_PIERCE_NONE /// hey uhhh don't hit anyone behind them
. = ..()
/proc/is_ctf_target(atom/target)
. = FALSE
if(istype(target, /obj/structure/barricade/security/ctf))
. = TRUE
- if(isliving(target))
- var/mob/living/H = target
- if((RED_TEAM in H.faction) || (BLUE_TEAM in H.faction))
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ if(istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/shielded/ctf))
. = TRUE
// RED TEAM GUNS
@@ -482,7 +500,11 @@
/obj/item/claymore/ctf/dropped(mob/user)
. = ..()
- addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
+ addtimer(CALLBACK(src, .proc/floor_vanish), 1)
+
+/obj/item/claymore/ctf/proc/floor_vanish()
+ if(isturf(loc))
+ qdel(src)
/datum/outfit/ctf
name = "CTF"
@@ -491,27 +513,28 @@
suit = /obj/item/clothing/suit/space/hardsuit/shielded/ctf
toggle_helmet = FALSE // see the whites of their eyes
shoes = /obj/item/clothing/shoes/combat
- gloves = /obj/item/clothing/gloves/tackler/combat
+ gloves = /obj/item/clothing/gloves/combat
+ id = /obj/item/card/id/away
belt = /obj/item/gun/ballistic/automatic/pistol/deagle/ctf
l_pocket = /obj/item/ammo_box/magazine/recharge/ctf
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf
back = /obj/item/claymore/ctf
-/datum/outfit/ctf/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
+/datum/outfit/ctf/post_equip(mob/living/carbon/human/H, visualsOnly=FALSE)
if(visualsOnly)
return
var/list/no_drops = list()
var/obj/item/card/id/W = H.wear_id
no_drops += W
W.registered_name = H.real_name
- W.update_label(W.registered_name, W.assignment)
+ W.update_label()
- // The shielded hardsuit is already TRAIT_NODROP
- no_drops += H.get_item_by_slot(SLOT_GLOVES)
- no_drops += H.get_item_by_slot(SLOT_SHOES)
- no_drops += H.get_item_by_slot(SLOT_W_UNIFORM)
- no_drops += H.get_item_by_slot(SLOT_EARS)
+ no_drops += H.get_item_by_slot(ITEM_SLOT_OCLOTHING)
+ no_drops += H.get_item_by_slot(ITEM_SLOT_GLOVES)
+ no_drops += H.get_item_by_slot(ITEM_SLOT_FEET)
+ no_drops += H.get_item_by_slot(ITEM_SLOT_ICLOTHING)
+ no_drops += H.get_item_by_slot(ITEM_SLOT_EARS)
for(var/i in no_drops)
var/obj/item/I = i
ADD_TRAIT(I, TRAIT_NODROP, CAPTURE_THE_FLAG_TRAIT)
@@ -525,6 +548,7 @@
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf/red
l_pocket = /obj/item/ammo_box/magazine/recharge/ctf/red
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf/red
+ id = /obj/item/card/id/syndicate_command //it's red
/datum/outfit/ctf/red/instagib
r_hand = /obj/item/gun/energy/laser/instakill/red
@@ -535,12 +559,13 @@
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf/blue
l_pocket = /obj/item/ammo_box/magazine/recharge/ctf/blue
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf/blue
+ id = /obj/item/card/id/centcom //it's blue
/datum/outfit/ctf/blue/instagib
r_hand = /obj/item/gun/energy/laser/instakill/blue
shoes = /obj/item/clothing/shoes/jackboots/fast
-/datum/outfit/ctf/red/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
+/datum/outfit/ctf/red/post_equip(mob/living/carbon/human/H)
..()
var/obj/item/radio/R = H.ears
R.set_frequency(FREQ_CTF_RED)
@@ -548,7 +573,7 @@
R.independent = TRUE
H.dna.species.stunmod = 0
-/datum/outfit/ctf/blue/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
+/datum/outfit/ctf/blue/post_equip(mob/living/carbon/human/H)
..()
var/obj/item/radio/R = H.ears
R.set_frequency(FREQ_CTF_BLUE)
@@ -595,6 +620,10 @@
/obj/structure/barricade/security/ctf/make_debris()
new /obj/effect/ctf/dead_barricade(get_turf(src))
+/obj/structure/table/reinforced/ctf
+ resistance_flags = INDESTRUCTIBLE
+ flags_1 = NODECONSTRUCT_1
+
/obj/effect/ctf
density = FALSE
anchored = TRUE
@@ -617,10 +646,11 @@
QDEL_IN(src, AMMO_DROP_LIFETIME)
/obj/effect/ctf/ammo/Crossed(atom/movable/AM)
+ . = ..()
reload(AM)
-/obj/effect/ctf/ammo/Bump(atom/movable/AM)
- reload(AM)
+/obj/effect/ctf/ammo/Bump(atom/A)
+ reload(A)
/obj/effect/ctf/ammo/Bumped(atom/movable/AM)
reload(AM)
@@ -636,7 +666,7 @@
qdel(G)
O.equip(M)
to_chat(M, "Ammunition reloaded!")
- playsound(get_turf(M), 'sound/weapons/shotgunpump.ogg', 50, 1, -1)
+ playsound(get_turf(M), 'sound/weapons/shotgunpump.ogg', 50, TRUE, -1)
qdel(src)
break
@@ -667,18 +697,18 @@
resistance_flags = INDESTRUCTIBLE
var/obj/machinery/capture_the_flag/controlling
var/team = "none"
- var/point_rate = 1
+ var/point_rate = 0.5
-/obj/machinery/control_point/process()
+/obj/machinery/control_point/process(delta_time)
if(controlling)
- controlling.control_points += point_rate
+ controlling.control_points += point_rate * delta_time
if(controlling.control_points >= controlling.control_points_to_win)
controlling.victory()
/obj/machinery/control_point/attackby(mob/user, params)
capture(user)
-/obj/machinery/control_point/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
+/obj/machinery/control_point/on_attack_hand(mob/user)
capture(user)
/obj/machinery/control_point/proc/capture(mob/user)
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index e68c45a84c..cd5c2f76f3 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -66,7 +66,7 @@
/obj/effect/mob_spawn/Initialize(mapload)
. = ..()
if(instant || (roundstart && (mapload || (SSticker && SSticker.current_state > GAME_STATE_SETTING_UP))))
- create()
+ INVOKE_ASYNC(src, .proc/create)
else if(ghost_usable)
GLOB.poi_list |= src
LAZYADD(GLOB.mob_spawners[job_description ? job_description : name], src)
diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm
index 129e6d7a2b..98e7f4c292 100644
--- a/code/modules/awaymissions/mission_code/Academy.dm
+++ b/code/modules/awaymissions/mission_code/Academy.dm
@@ -196,7 +196,7 @@
/obj/item/dice/d20/fate/proc/effect(var/mob/living/carbon/human/user,roll)
if(!reusable)
used = 1
- visible_message("The die flare briefly.")
+ visible_message("The die flares briefly.")
switch(roll)
if(1)
//Dust
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index f13341faf3..bc7e2cbd08 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -132,6 +132,7 @@
var/triggered = 0
/obj/effect/meatgrinder/Crossed(atom/movable/AM)
+ . = ..()
Bumped(AM)
/obj/effect/meatgrinder/Bumped(atom/movable/AM)
diff --git a/code/modules/cargo/bounties/reagent.dm b/code/modules/cargo/bounties/reagent.dm
index 391b4ff464..57501beab3 100644
--- a/code/modules/cargo/bounties/reagent.dm
+++ b/code/modules/cargo/bounties/reagent.dm
@@ -35,7 +35,7 @@
name = "Simple Drink"
reward = 1500
-datum/bounty/reagent/simple_drink/New()
+/datum/bounty/reagent/simple_drink/New()
// Don't worry about making this comprehensive. It doesn't matter if some drinks are skipped.
var/static/list/possible_reagents = list(\
/datum/reagent/consumable/ethanol/antifreeze,\
@@ -91,7 +91,7 @@ datum/bounty/reagent/simple_drink/New()
name = "Complex Drink"
reward = 4000
-datum/bounty/reagent/complex_drink/New()
+/datum/bounty/reagent/complex_drink/New()
// Don't worry about making this comprehensive. It doesn't matter if some drinks are skipped.
var/static/list/possible_reagents = list(\
/datum/reagent/consumable/ethanol/atomicbomb,\
@@ -124,7 +124,7 @@ datum/bounty/reagent/complex_drink/New()
reward = 2750
required_volume = 30
-datum/bounty/reagent/chemical/New()
+/datum/bounty/reagent/chemical/New()
// Don't worry about making this comprehensive. It doesn't matter if some chems are skipped.
var/static/list/possible_reagents = list(\
/datum/reagent/medicine/leporazine,\
diff --git a/code/modules/cargo/exports/gear.dm b/code/modules/cargo/exports/gear.dm
index 678948128f..8e93fc5fc3 100644
--- a/code/modules/cargo/exports/gear.dm
+++ b/code/modules/cargo/exports/gear.dm
@@ -473,7 +473,7 @@
//Soft Suits
//Blanket
-datum/export/gear/space/helmet
+/datum/export/gear/space/helmet
cost = 55
unit_name = "space helmet"
export_types = list(/obj/item/clothing/head/helmet/space)
@@ -485,7 +485,7 @@ datum/export/gear/space/helmet
export_types = list(/obj/item/clothing/suit/space)
include_subtypes = TRUE
-datum/export/gear/space/helmet/plasma
+/datum/export/gear/space/helmet/plasma
cost = 100
unit_name = "plasmaman space helmet"
export_types = list(/obj/item/clothing/suit/space/eva/plasmaman)
@@ -495,7 +495,7 @@ datum/export/gear/space/helmet/plasma
unit_name = "plasmaman space suit"
export_types = list(/obj/item/clothing/suit/space/eva/plasmaman)
-datum/export/gear/space/helmet/synda
+/datum/export/gear/space/helmet/synda
cost = 150 //Flash proof
unit_name = "syndicate space helmet"
export_types = list(/obj/item/clothing/head/helmet/space/syndicate)
@@ -510,7 +510,7 @@ datum/export/gear/space/helmet/synda
//Glasses
//Blanket
-datum/export/gear/glasses //glasses are not worth selling
+/datum/export/gear/glasses //glasses are not worth selling
cost = 3
unit_name = "glasses"
export_types = list(/obj/item/clothing/glasses)
diff --git a/code/modules/chatter/chatter.dm b/code/modules/chatter/chatter.dm
index 2dfd0bd77d..3da2b32a67 100644
--- a/code/modules/chatter/chatter.dm
+++ b/code/modules/chatter/chatter.dm
@@ -38,7 +38,7 @@
var/path = "sound/chatter/[phomeme]_[length].ogg"
playsound(loc, path,
- vol = 40, vary = 0, extrarange = 3, falloff = FALSE)
+ vol = 40, vary = 0, extrarange = 3)
sleep((length + 1) * chatter_get_sleep_multiplier(phomeme))
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index b71521121b..241cd6be93 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -31,6 +31,7 @@
var/datum/preferences/prefs = null
var/last_turn = 0
var/move_delay = 0
+ var/last_move = 0
var/area = null
/// Last time we Click()ed. No clicking twice in one tick!
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index c298d150f8..5f567f1360 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -147,6 +147,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
/client/proc/handle_statpanel_click(list/href_list)
var/atom/target = locate(href_list["statpanel_item_target"])
+ if(!target)
+ return
Click(target, target.loc, null, "[href_list["statpanel_item_shiftclick"]?"shift=1;":null][href_list["statpanel_item_ctrlclick"]?"ctrl=1;":null]&alt=[href_list["statpanel_item_altclick"]?"alt=1;":null]", FALSE, "statpanel")
/client/proc/is_content_unlocked()
@@ -496,7 +498,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
GLOB.directory -= ckey
log_access("Logout: [key_name(src)]")
GLOB.ahelp_tickets.ClientLogout(src)
- // SSserver_maint.UpdateHubStatus()
+ SSserver_maint.UpdateHubStatus()
if(credits)
QDEL_LIST(credits)
if(holder)
@@ -642,7 +644,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
query_log_connection.Execute()
qdel(query_log_connection)
- // SSserver_maint.UpdateHubStatus()
+ SSserver_maint.UpdateHubStatus()
if(new_player)
player_age = -1
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 7c6ba30b80..7cc8be800c 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -895,19 +895,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
continue
var/class_link = ""
var/list/loadout_item = has_loadout_gear(loadout_slot, "[gear.type]")
- var/extra_color_data = ""
+ var/extra_loadout_data = ""
if(loadout_item)
class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=0'"
if(gear.loadout_flags & LOADOUT_CAN_COLOR_POLYCHROMIC)
- extra_color_data += "
Color"
+ extra_loadout_data += "
Color"
for(var/loadout_color in loadout_item[LOADOUT_COLOR])
- extra_color_data += " "
+ extra_loadout_data += " "
else
var/loadout_color_non_poly = "#FFFFFF"
if(length(loadout_item[LOADOUT_COLOR]))
loadout_color_non_poly = loadout_item[LOADOUT_COLOR][1]
- extra_color_data += "
Color"
- extra_color_data += " "
+ extra_loadout_data += "
Color"
+ extra_loadout_data += " "
+ if(gear.loadout_flags & LOADOUT_CAN_NAME)
+ extra_loadout_data += "
Name [loadout_item[LOADOUT_CUSTOM_NAME] ? loadout_item[LOADOUT_CUSTOM_NAME] : "N/A"]"
+ if(gear.loadout_flags & LOADOUT_CAN_DESCRIPTION)
+ extra_loadout_data += "
Description"
else if((gear_points - gear.cost) < 0)
class_link = "style='white-space:normal;' class='linkOff'"
else if(donoritem)
@@ -916,7 +920,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
else
class_link = "style='white-space:normal;background:#eb2e2e;' class='linkOff'"
- dat += "[name][extra_color_data] "
+ dat += " "
+ // the below line essentially means "if the loadout item is picked by the user and has a custom description, give it the custom description, otherwise give it the default description"
+ dat += "[name][extra_loadout_data] "
dat += "[gear.cost] "
if(islist(gear.restricted_roles))
if(gear.restricted_roles.len)
@@ -929,14 +933,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += gear.restricted_roles.Join(";")
dat += ""
if(!istype(gear, /datum/gear/unlockable))
- dat += " [gear.description]