Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into why
This commit is contained in:
@@ -13,11 +13,20 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup cache
|
||||
- name: Restore SpacemanDMM cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: $HOME/SpacemanDMM
|
||||
key: ${{ runner.os }}-spacemandmm
|
||||
path: ~/SpacemanDMM
|
||||
key: ${{ runner.os }}-spacemandmm-${{ secrets.CACHE_PURGE_KEY }}
|
||||
- name: Restore Yarn cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: tgui/.yarn/cache
|
||||
key: ${{ runner.os }}-yarn-${{ secrets.CACHE_PURGE_KEY }}-${{ hashFiles('tgui/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ secrets.CACHE_PURGE_KEY }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Install Tools
|
||||
run: |
|
||||
pip3 install setuptools
|
||||
@@ -28,11 +37,10 @@ jobs:
|
||||
run: |
|
||||
bash tools/ci/check_filedirs.sh tgstation.dme
|
||||
bash tools/ci/check_changelogs.sh
|
||||
bash tools/ci/check_grep.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
|
||||
tgui/bin/tgui --lint
|
||||
tgui/bin/tgui --test
|
||||
bash tools/ci/check_grep.sh
|
||||
tools/build/build --ci lint
|
||||
tools/bootstrap/python -m dmi.test
|
||||
tools/bootstrap/python -m mapmerge2.dmm_test
|
||||
~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1
|
||||
@@ -48,19 +56,16 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup cache
|
||||
- name: Restore BYOND cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: $HOME/BYOND
|
||||
key: ${{ runner.os }}-byond
|
||||
path: ~/BYOND
|
||||
key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }}
|
||||
- name: Compile All Maps
|
||||
run: |
|
||||
bash tools/ci/install_byond.sh
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
python3 tools/ci/template_dm_generator.py
|
||||
tools/build/build
|
||||
env:
|
||||
CBT_BUILD_MODE : ALL_MAPS
|
||||
tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS
|
||||
|
||||
run_all_tests:
|
||||
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
||||
@@ -76,11 +81,20 @@ jobs:
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup cache
|
||||
- name: Restore BYOND cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: $HOME/BYOND
|
||||
key: ${{ runner.os }}-byond
|
||||
path: ~/BYOND
|
||||
key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }}
|
||||
- name: Restore Yarn cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: tgui/.yarn/cache
|
||||
key: ${{ runner.os }}-yarn-${{ secrets.CACHE_PURGE_KEY }}-${{ hashFiles('tgui/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ secrets.CACHE_PURGE_KEY }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Setup database
|
||||
run: |
|
||||
sudo systemctl start mysql
|
||||
@@ -88,7 +102,7 @@ jobs:
|
||||
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 dependencies
|
||||
- name: Install rust-g
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt update || true
|
||||
@@ -96,15 +110,14 @@ jobs:
|
||||
bash tools/ci/install_rust_g.sh
|
||||
- name: Install auxmos
|
||||
run: |
|
||||
sudo apt update || true
|
||||
bash tools/ci/install_auxmos.sh
|
||||
- name: Compile and run tests
|
||||
run: |
|
||||
bash tools/ci/install_byond.sh
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
tools/build/build -DCIBUILDING
|
||||
tools/build/build --ci -DCIBUILDING
|
||||
bash tools/ci/run_server.sh
|
||||
env:
|
||||
CBT_BUILD_MODE: TEST_RUN
|
||||
|
||||
test_windows:
|
||||
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
||||
@@ -112,6 +125,15 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Restore Yarn cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: tgui/.yarn/cache
|
||||
key: ${{ runner.os }}-yarn-${{ secrets.CACHE_PURGE_KEY }}-${{ hashFiles('tgui/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ secrets.CACHE_PURGE_KEY }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- name: Compile
|
||||
run: pwsh tools/ci/build.ps1
|
||||
env:
|
||||
|
||||
@@ -12,8 +12,8 @@ jobs:
|
||||
- name: Setup cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: $HOME/SpacemanDMM
|
||||
key: ${{ runner.os }}-spacemandmm
|
||||
path: ~/SpacemanDMM
|
||||
key: ${{ runner.os }}-spacemandmm-${{ secrets.CACHE_PURGE_KEY }}
|
||||
- name: Install SpacemanDMM
|
||||
run: bash tools/ci/install_spaceman_dmm.sh dmdoc
|
||||
- name: Generate documentation
|
||||
|
||||
+17
-59
@@ -7,14 +7,16 @@
|
||||
#Ignore byond config folder.
|
||||
/cfg/**/*
|
||||
|
||||
#Ignore rust-g and auxmos libraries which are compiled with scripts
|
||||
*.so
|
||||
/tools/build/binaries/**/*
|
||||
# Ignore compiled linux libs in the root folder, e.g. librust_g.so
|
||||
/*.so
|
||||
|
||||
#Ignore compiled files and other files generated during compilation.
|
||||
*.mdme
|
||||
*.mdme.*
|
||||
*.dmb
|
||||
*.rsc
|
||||
*.m.dme
|
||||
*.test.dme
|
||||
*.lk
|
||||
*.int
|
||||
*.backup
|
||||
@@ -53,27 +55,6 @@ __pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
#*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
@@ -81,8 +62,7 @@ var/
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
pip-*.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
@@ -92,7 +72,6 @@ htmlcov/
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
@@ -101,10 +80,6 @@ coverage.xml
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask instance folder
|
||||
instance/
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
@@ -112,9 +87,6 @@ instance/
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# IPython Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
@@ -127,10 +99,6 @@ celerybeat-schedule
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# IntelliJ IDEA / PyCharm (with plugin)
|
||||
.idea
|
||||
|
||||
@@ -153,12 +121,6 @@ Desktop.ini
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
#*.cab
|
||||
#*.msi
|
||||
#*.msm
|
||||
#*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
@@ -199,11 +161,10 @@ Temporary Items
|
||||
|
||||
#Visual studio stuff
|
||||
*.vscode/*
|
||||
!/.vscode/extensions.json
|
||||
tools/MapAtmosFixer/MapAtmosFixer/obj/*
|
||||
tools/MapAtmosFixer/MapAtmosFixer/bin/*
|
||||
tools/CreditsTool/bin/*
|
||||
tools/CreditsTool/obj/*
|
||||
/tools/MapAtmosFixer/MapAtmosFixer/obj/*
|
||||
/tools/MapAtmosFixer/MapAtmosFixer/bin/*
|
||||
/tools/CreditsTool/bin/*
|
||||
/tools/CreditsTool/obj/*
|
||||
|
||||
#GitHub Atom
|
||||
.atom-build.json
|
||||
@@ -228,13 +189,10 @@ tools/CreditsTool/obj/*
|
||||
!/config/title_screens/images/exclude
|
||||
|
||||
#Linux docker
|
||||
tools/LinuxOneShot/SetupProgram/obj/*
|
||||
tools/LinuxOneShot/SetupProgram/bin/*
|
||||
tools/LinuxOneShot/SetupProgram/.vs
|
||||
tools/LinuxOneShot/Database
|
||||
tools/LinuxOneShot/TGS_Config
|
||||
tools/LinuxOneShot/TGS_Instances
|
||||
tools/LinuxOneShot/TGS_Logs
|
||||
|
||||
# Common build tooling
|
||||
!/tools/build
|
||||
/tools/LinuxOneShot/SetupProgram/obj/*
|
||||
/tools/LinuxOneShot/SetupProgram/bin/*
|
||||
/tools/LinuxOneShot/SetupProgram/.vs
|
||||
/tools/LinuxOneShot/Database
|
||||
/tools/LinuxOneShot/TGS_Config
|
||||
/tools/LinuxOneShot/TGS_Instances
|
||||
/tools/LinuxOneShot/TGS_Logs
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
@call tools\build\build
|
||||
@pause
|
||||
@echo off
|
||||
call "%~dp0\tools\build\build.bat" %*
|
||||
pause
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
call "%~dp0\tools\build\build.bat" dist-clean
|
||||
pause
|
||||
@@ -1,7 +1,7 @@
|
||||
## Citadel Station 13
|
||||
Based and maintained from /tg/station.
|
||||
|
||||
[](https://travis-ci.org/Citadel-Station-13/Citadel-Station-13)
|
||||
[](https://github.com/Citadel-Station-13/Citadel-Station-13/actions?query=workflow%3A%22CI+Suite%22)
|
||||
[](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Percentage of issues still open")
|
||||
[](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Average time to resolve an issue")
|
||||
|
||||
@@ -100,6 +100,14 @@ install, overwriting when prompted except if we've specified otherwise, and
|
||||
recompile the game. Once you start the server up again, you should be running
|
||||
the new version.
|
||||
|
||||
## :exclamation: How to compile :exclamation:
|
||||
|
||||
On **2021-01-04** we have changed the way to compile the codebase.
|
||||
|
||||
Find `Build.cmd` in this folder, and double click it to initiate the build. It consists of multiple steps and might take around 1-5 minutes to compile. If it closes, it means it has finished its job. You can then setup the server normally by opening `tgstation.dmb` in DreamDaemon.
|
||||
|
||||
**Building tgstation in DreamMaker directly is now deprecated and might produce errors**, such as `'tgui.bundle.js': cannot find file`.
|
||||
|
||||
## HOSTING
|
||||
|
||||
If you'd like a more robust server hosting option for tgstation and its
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
call "%~dp0\tools\build\build.bat" server %*
|
||||
pause
|
||||
@@ -54548,8 +54548,8 @@
|
||||
id = "xenoigniter";
|
||||
luminosity = 2
|
||||
},
|
||||
/obj/machinery/atmospherics/components/unary/vent_pump/on{
|
||||
dir = 8
|
||||
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/science/xenobiology)
|
||||
@@ -55484,7 +55484,7 @@
|
||||
/obj/structure/cable{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/manifold/supply/hidden,
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden,
|
||||
/turf/open/floor/plasteel/dark,
|
||||
/area/science/xenobiology)
|
||||
"hVx" = (
|
||||
@@ -55906,6 +55906,7 @@
|
||||
id = "xenoigniter";
|
||||
luminosity = 2
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
|
||||
/turf/open/floor/engine,
|
||||
/area/science/xenobiology)
|
||||
"iPz" = (
|
||||
@@ -59460,6 +59461,7 @@
|
||||
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
|
||||
dir = 8
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
|
||||
/turf/open/floor/engine,
|
||||
/area/science/xenobiology)
|
||||
"qAx" = (
|
||||
@@ -60664,6 +60666,7 @@
|
||||
/area/maintenance/department/science)
|
||||
"tfP" = (
|
||||
/obj/item/beacon,
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
|
||||
/turf/open/floor/engine,
|
||||
/area/science/xenobiology)
|
||||
"tfZ" = (
|
||||
@@ -114714,13 +114717,13 @@ abI
|
||||
aby
|
||||
aaa
|
||||
xKc
|
||||
blX
|
||||
lCN
|
||||
qAk
|
||||
iPj
|
||||
tfP
|
||||
geN
|
||||
qAk
|
||||
blX
|
||||
uzB
|
||||
xKc
|
||||
aaa
|
||||
aby
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -437,8 +437,9 @@
|
||||
#define COMPONENT_BLOCK_SHARPEN_BLOCKED 2
|
||||
#define COMPONENT_BLOCK_SHARPEN_ALREADY 4
|
||||
#define COMPONENT_BLOCK_SHARPEN_MAXED 8
|
||||
#define COMSIG_ITEM_MICROWAVE_ACT "microwave_act" //called on item when microwaved (): (obj/machinery/microwave/M)
|
||||
#define COMSIG_ITEM_MICROWAVE_ACT "microwave_act" //called on item when microwaved (): (obj/machinery/microwave/M)
|
||||
#define COMSIG_ITEM_WORN_OVERLAYS "item_worn_overlays" //from base of obj/item/worn_overlays(): (isinhands, icon_file, used_state, style_flags, list/overlays)
|
||||
#define COMSIG_ARMOR_PLATED "armor_plated" //called when an armor plate is successfully applied to an object
|
||||
// THE FOLLOWING TWO BLOCKS SHOULD RETURN BLOCK FLAGS AS DEFINED IN __DEFINES/combat.dm!
|
||||
#define COMSIG_ITEM_CHECK_BLOCK "check_block" //from base of obj/item/check_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
#define COMSIG_ITEM_RUN_BLOCK "run_block" //from base of obj/item/run_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
|
||||
+28
-14
@@ -1,30 +1,44 @@
|
||||
//defines that give qdel hints. these can be given as a return in destory() or by calling
|
||||
//! Defines that give qdel hints.
|
||||
//!
|
||||
//! These can be given as a return in [/atom/proc/Destroy] or by calling [/proc/qdel].
|
||||
|
||||
/// `qdel` should queue the object for deletion.
|
||||
#define QDEL_HINT_QUEUE 0
|
||||
/// `qdel` should let the object live after calling [/atom/proc/Destroy].
|
||||
#define QDEL_HINT_LETMELIVE 1
|
||||
/// Functionally the same as the above. `qdel` should assume the object will gc on its own, and not check it.
|
||||
#define QDEL_HINT_IWILLGC 2
|
||||
/// Qdel should assume this object won't GC, and queue a hard delete using a hard reference.
|
||||
#define QDEL_HINT_HARDDEL 3
|
||||
// Qdel should assume this object won't gc, and hard delete it posthaste.
|
||||
#define QDEL_HINT_HARDDEL_NOW 4
|
||||
|
||||
|
||||
#define QDEL_HINT_QUEUE 0 //qdel should queue the object for deletion.
|
||||
#define QDEL_HINT_LETMELIVE 1 //qdel should let the object live after calling destory.
|
||||
#define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it.
|
||||
#define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference.
|
||||
#define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste.
|
||||
|
||||
#ifdef LEGACY_REFERENCE_TRACKING
|
||||
/** If LEGACY_REFERENCE_TRACKING is enabled, qdel will call this object's find_references() verb.
|
||||
*
|
||||
* Functionally identical to QDEL_HINT_QUEUE if GC_FAILURE_HARD_LOOKUP is not enabled in _compiler_options.dm.
|
||||
#ifdef REFERENCE_TRACKING
|
||||
/** If REFERENCE_TRACKING is enabled, qdel will call this object's find_references() verb.
|
||||
*
|
||||
* Functionally identical to [QDEL_HINT_QUEUE] if [GC_FAILURE_HARD_LOOKUP] is not enabled in _compiler_options.dm.
|
||||
*/
|
||||
#define QDEL_HINT_FINDREFERENCE 5
|
||||
/// Behavior as QDEL_HINT_FINDREFERENCE, but only if the GC fails and a hard delete is forced.
|
||||
#define QDEL_HINT_FINDREFERENCE 5
|
||||
/// Behavior as [QDEL_HINT_FINDREFERENCE], but only if the GC fails and a hard delete is forced.
|
||||
#define QDEL_HINT_IFFAIL_FINDREFERENCE 6
|
||||
#endif
|
||||
|
||||
|
||||
#define GC_QUEUE_CHECK 1
|
||||
#define GC_QUEUE_HARDDELETE 2
|
||||
#define GC_QUEUE_COUNT 2 //increase this when adding more steps.
|
||||
|
||||
#define QDEL_ITEM_ADMINS_WARNED (1<<0) //! Set when admins are told about lag causing qdels in this type.
|
||||
#define QDEL_ITEM_SUSPENDED_FOR_LAG (1<<1) //! Set when a type can no longer be hard deleted on failure because of lag it causes while this happens.
|
||||
|
||||
// Defines for the [gc_destroyed][/datum/var/gc_destroyed] var.
|
||||
#define GC_QUEUED_FOR_QUEUING -1
|
||||
#define GC_CURRENTLY_BEING_QDELETED -2
|
||||
|
||||
// Defines for the time left for an item to get its reference cleaned
|
||||
#define GC_FILTER_QUEUE 5 MINUTES
|
||||
#define GC_DEL_QUEUE 10 SECONDS
|
||||
|
||||
#define QDELING(X) (X.gc_destroyed)
|
||||
#define QDELETED(X) (!X || QDELING(X))
|
||||
#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
#define RUST_G (__rust_g || __detect_rust_g())
|
||||
#endif
|
||||
|
||||
/// Gets the version of rust_g
|
||||
/proc/rustg_get_version() return call(RUST_G, "get_version")()
|
||||
|
||||
/**
|
||||
* This proc generates a cellular automata noise grid which can be used in procedural generation methods.
|
||||
*
|
||||
@@ -77,8 +80,8 @@
|
||||
#define RUSTG_HTTP_METHOD_PATCH "patch"
|
||||
#define RUSTG_HTTP_METHOD_HEAD "head"
|
||||
#define RUSTG_HTTP_METHOD_POST "post"
|
||||
#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_request_blocking(method, url, body, headers, options) call(RUST_G, "http_request_blocking")(method, url, body, headers, options)
|
||||
#define rustg_http_request_async(method, url, body, headers, options) call(RUST_G, "http_request_async")(method, url, body, headers, options)
|
||||
#define rustg_http_check_request(req_id) call(RUST_G, "http_check_request")(req_id)
|
||||
|
||||
#define RUSTG_JOB_NO_RESULTS_YET "NO RESULTS YET"
|
||||
@@ -99,3 +102,11 @@
|
||||
#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]")
|
||||
|
||||
#define rustg_url_encode(text) call(RUST_G, "url_encode")(text)
|
||||
#define rustg_url_decode(text) call(RUST_G, "url_decode")(text)
|
||||
|
||||
#ifdef RUSTG_OVERRIDE_BUILTINS
|
||||
#define url_encode(text) rustg_url_encode(text)
|
||||
#define url_decode(text) rustg_url_decode(text)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// RUSTG_OVERRIDE_BUILTINS is not used since the file APIs don't work well over Linux.
|
||||
#define url_encode(text) rustg_url_encode("[text]")
|
||||
#define url_decode(text) rustg_url_decode("[text]")
|
||||
@@ -49,10 +49,16 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using
|
||||
|
||||
#define TICKS *world.tick_lag
|
||||
|
||||
#define MILLISECONDS * 0.01
|
||||
|
||||
#define DS2TICKS(DS) ((DS)/world.tick_lag)
|
||||
|
||||
#define TICKS2DS(T) ((T) TICKS)
|
||||
|
||||
#define MS2DS(T) ((T) MILLISECONDS)
|
||||
|
||||
#define DS2MS(T) ((T) * 100)
|
||||
|
||||
#define GAMETIMESTAMP(format, wtime) time2text(wtime, format)
|
||||
#define WORLDTIME2TEXT(format) GAMETIMESTAMP(format, world.time)
|
||||
#define WORLDTIMEOFDAY2TEXT(format) GAMETIMESTAMP(format, world.timeofday)
|
||||
|
||||
@@ -343,3 +343,7 @@
|
||||
#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name
|
||||
#define TRAIT_SACRIFICED "sacrificed" //Makes sure that people cant be cult sacrificed twice.
|
||||
#define TRAIT_SPACEWALK "spacewalk"
|
||||
|
||||
|
||||
/// obtained from mapping helper
|
||||
#define MAPPING_HELPER_TRAIT "mapping-helper"
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
//wrapper macros for easier grepping
|
||||
#define DIRECT_OUTPUT(A, B) A << B
|
||||
#define DIRECT_INPUT(A, B) A >> B
|
||||
#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image)
|
||||
#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound)
|
||||
#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text)
|
||||
#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text)
|
||||
#define READ_FILE(file, text) DIRECT_INPUT(file, text)
|
||||
|
||||
#ifdef EXTOOLS_LOGGING
|
||||
// proc hooked, so we can just put in standard TRUE and FALSE
|
||||
#define WRITE_LOG(log, text) extools_log_write(log,text,TRUE)
|
||||
@@ -13,6 +16,7 @@
|
||||
#define WRITE_LOG(log, text) rustg_log_write(log, text, "true")
|
||||
#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false")
|
||||
#endif
|
||||
|
||||
//print a warning message to world.log
|
||||
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].")
|
||||
/proc/warning(msg)
|
||||
@@ -38,6 +42,12 @@
|
||||
SEND_TEXT(world.log, text)
|
||||
#endif
|
||||
|
||||
#ifdef REFERENCE_TRACKING_LOG
|
||||
#define log_reftracker(msg) log_world("## REF SEARCH [msg]")
|
||||
#else
|
||||
#define log_reftracker(msg)
|
||||
#endif
|
||||
|
||||
|
||||
/* Items with ADMINPRIVATE prefixed are stripped from public logs. */
|
||||
/proc/log_admin(text)
|
||||
|
||||
@@ -78,6 +78,12 @@
|
||||
var/datum/emote/E = new path()
|
||||
E.emote_list[E.key] = E
|
||||
|
||||
// Hair Gradients - Initialise all /datum/sprite_accessory/hair_gradient into an list indexed by gradient-style name
|
||||
for(var/path in subtypesof(/datum/sprite_accessory/hair_gradient))
|
||||
var/datum/sprite_accessory/hair_gradient/H = new path()
|
||||
GLOB.hair_gradients_list[H.name] = H
|
||||
|
||||
// Keybindings
|
||||
init_keybindings()
|
||||
|
||||
//Uplink Items
|
||||
|
||||
+13
-13
@@ -218,20 +218,14 @@
|
||||
|
||||
to_chat(world, "<BR><BR><BR><span class='big bold'>The round has ended.</span>")
|
||||
log_game("The round has ended.")
|
||||
if(LAZYLEN(GLOB.round_end_notifiees))
|
||||
world.TgsTargetedChatBroadcast("[GLOB.round_end_notifiees.Join(", ")] the round has ended.", FALSE)
|
||||
|
||||
CONFIG_SET(flag/suicide_allowed,TRUE) // EORG suicides allowed
|
||||
|
||||
for(var/I in round_end_events)
|
||||
var/datum/callback/cb = I
|
||||
cb.InvokeAsync()
|
||||
LAZYCLEARLIST(round_end_events)
|
||||
|
||||
for(var/client/C in GLOB.clients)
|
||||
if(!C.credits)
|
||||
C.RollCredits()
|
||||
C.playtitlemusic(40)
|
||||
CONFIG_SET(flag/suicide_allowed,TRUE) // EORG suicides allowed
|
||||
|
||||
var/speed_round = FALSE
|
||||
if(world.time - SSticker.round_start_time <= 300 SECONDS)
|
||||
speed_round = TRUE
|
||||
@@ -264,12 +258,15 @@
|
||||
|
||||
send2adminchat("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
|
||||
|
||||
if(LAZYLEN(GLOB.round_end_notifiees))
|
||||
world.TgsTargetedChatBroadcast("[GLOB.round_end_notifiees.Join(", ")] the round has ended.", FALSE)
|
||||
|
||||
if(length(CONFIG_GET(keyed_list/cross_server)))
|
||||
send_news_report()
|
||||
|
||||
//tell the nice people on discord what went on before the salt cannon happens.
|
||||
world.TgsTargetedChatBroadcast("The current round has ended. Please standby for your shift interlude Nanotrasen News Network's report!", FALSE)
|
||||
world.TgsTargetedChatBroadcast(send_news_report(),FALSE)
|
||||
world.TgsTargetedChatBroadcast(send_news_report(), FALSE)
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
@@ -543,21 +540,24 @@
|
||||
///Generate a report for how much money is on station, as well as the richest crewmember on the station.
|
||||
/datum/controller/subsystem/ticker/proc/market_report()
|
||||
var/list/parts = list()
|
||||
parts += "<span class='header'>Station Economic Summary:</span>"
|
||||
|
||||
///This is the richest account on station at roundend.
|
||||
var/datum/bank_account/mr_moneybags
|
||||
///This is the station's total wealth at the end of the round.
|
||||
var/station_vault = 0
|
||||
///How many players joined the round.
|
||||
var/total_players = GLOB.joined_player_list.len
|
||||
var/list/typecache_bank = typecacheof(list(/datum/bank_account/department, /datum/bank_account/remote))
|
||||
for(var/datum/bank_account/current_acc in SSeconomy.generated_accounts)
|
||||
var/static/list/typecache_bank = typecacheof(list(/datum/bank_account/department, /datum/bank_account/remote))
|
||||
for(var/i in SSeconomy.generated_accounts)
|
||||
var/datum/bank_account/current_acc = SSeconomy.generated_accounts[i]
|
||||
if(typecache_bank[current_acc.type])
|
||||
continue
|
||||
station_vault += current_acc.account_balance
|
||||
if(!mr_moneybags || mr_moneybags.account_balance < current_acc.account_balance)
|
||||
mr_moneybags = current_acc
|
||||
parts += "<div class='panel stationborder'>There were [station_vault] credits collected by crew this shift.<br>"
|
||||
parts += "<div class='panel stationborder'><span class='header'>Station Economic Summary:</span><br>"
|
||||
parts += "<b>General Statistics:</b><br>"
|
||||
parts += "There were [station_vault] credits collected by crew this shift.<br>"
|
||||
if(total_players > 0)
|
||||
parts += "An average of [station_vault/total_players] credits were collected.<br>"
|
||||
// log_econ("Roundend credit total: [station_vault] credits. Average Credits: [station_vault/total_players]")
|
||||
|
||||
+47
-23
@@ -1,7 +1,7 @@
|
||||
//#define TESTING //By using the testing("message") proc you can create debug-feedback for people with this
|
||||
//#define TESTING //By using the testing("message") proc you can create debug-feedback for people with this
|
||||
//uncommented, but not visible in the release version)
|
||||
|
||||
//#define DATUMVAR_DEBUGGING_MODE //Enables the ability to cache datum vars and retrieve later for debugging which vars changed.
|
||||
//#define DATUMVAR_DEBUGGING_MODE //Enables the ability to cache datum vars and retrieve later for debugging which vars changed.
|
||||
|
||||
// Comment this out if you are debugging problems that might be obscured by custom error handling in world/Error
|
||||
#ifdef DEBUG
|
||||
@@ -11,34 +11,44 @@
|
||||
#ifdef TESTING
|
||||
#define DATUMVAR_DEBUGGING_MODE
|
||||
|
||||
/*
|
||||
* Enables extools-powered reference tracking system, letting you see what is referencing objects that refuse to hard delete.
|
||||
*
|
||||
* * Requires TESTING to be defined to work.
|
||||
*/
|
||||
///Used to find the sources of harddels, quite laggy, don't be surpised if it freezes your client for a good while
|
||||
//#define REFERENCE_TRACKING
|
||||
#ifdef REFERENCE_TRACKING
|
||||
|
||||
///Method of tracking references without using extools. Slower, kept to avoid over-reliance on extools.
|
||||
//#define LEGACY_REFERENCE_TRACKING
|
||||
#ifdef LEGACY_REFERENCE_TRACKING
|
||||
///alternate to reftracking, extool variant
|
||||
//#define EXTOOLS_REFERENCE_TRACKING
|
||||
|
||||
///Use the legacy reference on things hard deleting by default.
|
||||
///Should we be logging our findings or not
|
||||
#define REFERENCE_TRACKING_LOG
|
||||
|
||||
///Used for doing dry runs of the reference finder, to test for feature completeness
|
||||
//#define REFERENCE_TRACKING_DEBUG
|
||||
|
||||
///Run a lookup on things hard deleting by default.
|
||||
//#define GC_FAILURE_HARD_LOOKUP
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
#define FIND_REF_NO_CHECK_TICK
|
||||
#endif //ifdef GC_FAILURE_HARD_LOOKUP
|
||||
|
||||
#endif //ifdef LEGACY_REFERENCE_TRACKING
|
||||
#endif //ifdef REFERENCE_TRACKING
|
||||
|
||||
//#define VISUALIZE_ACTIVE_TURFS //Highlights atmos active turfs in green
|
||||
/*
|
||||
* Enables debug messages for every single reaction step. This is 1 message per 0.5s for a SINGLE reaction. Useful for tracking down bugs/asking me for help in the main reaction handiler (equilibrium.dm).
|
||||
*
|
||||
* * Requires TESTING to be defined to work.
|
||||
*/
|
||||
//#define REAGENTS_TESTING
|
||||
|
||||
// #define VISUALIZE_ACTIVE_TURFS //Highlights atmos active turfs in green
|
||||
// #define TRACK_MAX_SHARE //Allows max share tracking, for use in the atmos debugging ui
|
||||
#endif //ifdef TESTING
|
||||
//#define UNIT_TESTS //Enables unit tests via TEST_RUN_PARAMETER
|
||||
|
||||
#ifndef PRELOAD_RSC //set to:
|
||||
#define PRELOAD_RSC 2 // 0 to allow using external resources or on-demand behaviour;
|
||||
#endif // 1 to use the default behaviour;
|
||||
// 2 for preloading absolutely everything;
|
||||
//#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between
|
||||
|
||||
#ifndef PRELOAD_RSC //set to:
|
||||
#define PRELOAD_RSC 2 // 0 to allow using external resources or on-demand behaviour;
|
||||
#endif // 1 to use the default behaviour;
|
||||
// 2 for preloading absolutely everything;
|
||||
|
||||
#ifdef LOWMEMORYMODE
|
||||
#define FORCE_MAP "_maps/runtimestation.json"
|
||||
@@ -47,7 +57,7 @@
|
||||
//Update this whenever you need to take advantage of more recent byond features
|
||||
#define MIN_COMPILER_VERSION 513
|
||||
#define MIN_COMPILER_BUILD 1514
|
||||
#if DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD
|
||||
#if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM)
|
||||
//Don't forget to update this part
|
||||
#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update.
|
||||
#error You need version 513.1514 or higher
|
||||
@@ -58,10 +68,6 @@
|
||||
#warn compiling in TESTING mode. testing() debug messages will be visible.
|
||||
#endif
|
||||
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
#define FIND_REF_NO_CHECK_TICK
|
||||
#endif
|
||||
|
||||
#ifdef CIBUILDING
|
||||
#define UNIT_TESTS
|
||||
#endif
|
||||
@@ -70,6 +76,24 @@
|
||||
#define TESTING
|
||||
#endif
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
//Hard del testing defines
|
||||
#define REFERENCE_TRACKING
|
||||
#define REFERENCE_TRACKING_DEBUG
|
||||
#define FIND_REF_NO_CHECK_TICK
|
||||
#endif
|
||||
|
||||
#ifdef TGS
|
||||
// TGS performs its own build of dm.exe, but includes a prepended TGS define.
|
||||
#define CBT
|
||||
#endif
|
||||
|
||||
// A reasonable number of maximum overlays an object needs
|
||||
// If you think you need more, rethink it
|
||||
#define MAX_ATOM_OVERLAYS 100
|
||||
|
||||
#if !defined(CBT) && !defined(SPACEMAN_DMM)
|
||||
#warn Building with Dream Maker is no longer supported and will result in errors.
|
||||
#warn In order to build, run BUILD.bat in the root directory.
|
||||
#warn Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build.
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@ GLOBAL_LIST_EMPTY(hair_styles_female_list) //stores only hair names
|
||||
GLOBAL_LIST_EMPTY(facial_hair_styles_list) //stores /datum/sprite_accessory/facial_hair indexed by name
|
||||
GLOBAL_LIST_EMPTY(facial_hair_styles_male_list) //stores only hair names
|
||||
GLOBAL_LIST_EMPTY(facial_hair_styles_female_list) //stores only hair names
|
||||
GLOBAL_LIST_EMPTY(hair_gradients_list) //stores /datum/sprite_accessory/hair_gradient indexed by name
|
||||
//Underwear
|
||||
GLOBAL_LIST_EMPTY_TYPED(underwear_list, /datum/sprite_accessory/underwear/bottom) //stores bottoms indexed by name
|
||||
GLOBAL_LIST_EMPTY(underwear_m) //stores only underwear name
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
return ShiftClickOn(A)
|
||||
if(modifiers["alt"]) // alt and alt-gr (rightalt)
|
||||
return AltClickOn(A)
|
||||
if(modifiers["ctrl"] && modifiers["right"]) //CIT CHANGE - right click ctrl for a new form of dropping items
|
||||
return CtrlRightClickOn(A, params) //CIT CHANGE
|
||||
if(modifiers["ctrl"])
|
||||
return CtrlClickOn(A)
|
||||
|
||||
@@ -295,6 +297,46 @@
|
||||
if(!(flags & COMPONENT_DENY_EXAMINATE) && user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE))
|
||||
user.examinate(src)
|
||||
|
||||
/*
|
||||
Ctrl + Right click
|
||||
Combat mode feature
|
||||
Drop item in hand at position.
|
||||
*/
|
||||
/atom/proc/CtrlRightClickOn(atom/A, params)
|
||||
if(isliving(src) && Adjacent(A)) //honestly only humans can do this given it's combat mode but if it's implemented for any other mobs...
|
||||
var/mob/living/L = src
|
||||
if(L.incapacitated())
|
||||
return
|
||||
var/obj/item/I = L.get_active_held_item()
|
||||
var/turf/T = get_turf(A)
|
||||
if(T)
|
||||
if(I) //drop item at cursor.
|
||||
if(T.density) //no, you can't use your funny blue cube or red cube to clip into the fucking wall.
|
||||
return
|
||||
for(var/atom/C in T.contents) //nor can you clip into a window or a door/false wall that's not open.
|
||||
if(C.opacity || (((C.flags_1 & PREVENT_CLICK_UNDER_1) > 0) != (istype(C,/obj/machinery/door) && !C.density))) //XOR operation within because doors always have PREVENT_CLICK_UNDER_1 flag enabled. Dumb, I know.
|
||||
return
|
||||
if(L.transferItemToLoc(I, T))
|
||||
var/list/click_params = params2list(params)
|
||||
//Center the icon where the user clicked. (shamelessly stole code from tables)
|
||||
if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
|
||||
return
|
||||
//Clamp it so that the icon never moves more than 16 pixels in either direction
|
||||
I.pixel_x = clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
|
||||
I.pixel_y = clamp(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2)
|
||||
return TRUE
|
||||
else if(isitem(A) && L.has_active_hand()) //if they have an open hand they'll rotate the item instead.
|
||||
var/obj/item/I2 = A
|
||||
if(!I2.anchored)
|
||||
var/matrix/ntransform = matrix(I2.transform)
|
||||
ntransform.Turn(15)
|
||||
animate(I2, transform = ntransform, time = 2)
|
||||
return TRUE
|
||||
else
|
||||
A.CtrlClick(src)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Ctrl click
|
||||
For most objects, pull
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
INVOKE_ASYNC(aicamera, /obj/item/camera.proc/captureimage, A, usr)
|
||||
return
|
||||
|
||||
var/obj/item/W = get_active_held_item()
|
||||
var/obj/item/W = get_active_held_item(TRUE)
|
||||
|
||||
if(!W && A.Adjacent(src) && (isobj(A) || ismob(A)))
|
||||
var/atom/movable/C = A
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
// Clickable stat() button.
|
||||
/obj/effect/statclick
|
||||
name = "Initializing..."
|
||||
// blocks_emissive = NONE
|
||||
var/target
|
||||
|
||||
INITIALIZE_IMMEDIATE(/obj/effect/statclick)
|
||||
|
||||
/obj/effect/statclick/Initialize(mapload, text, target) //Don't port this to Initialize it's too critical
|
||||
/obj/effect/statclick/Initialize(mapload, text, target)
|
||||
. = ..()
|
||||
name = text
|
||||
src.target = target
|
||||
if(istype(target, /datum)) //Harddel man bad
|
||||
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/cleanup)
|
||||
|
||||
/obj/effect/statclick/Destroy()
|
||||
target = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/statclick/proc/cleanup()
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/statclick/proc/update(text)
|
||||
name = text
|
||||
@@ -51,3 +62,30 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Restart Failsafe Controller")
|
||||
|
||||
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
|
||||
|
||||
/client/proc/debug_controller()
|
||||
set category = "Debug"
|
||||
set name = "Debug Controller"
|
||||
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
|
||||
|
||||
if(!holder)
|
||||
return
|
||||
|
||||
var/list/controllers = list()
|
||||
var/list/controller_choices = list()
|
||||
|
||||
for (var/datum/controller/controller in world)
|
||||
if (istype(controller, /datum/controller/subsystem))
|
||||
continue
|
||||
controllers["[controller] (controller.type)"] = controller //we use an associated list to ensure clients can't hold references to controllers
|
||||
controller_choices += "[controller] (controller.type)"
|
||||
|
||||
var/datum/controller/controller_string = input("Select controller to debug", "Debug Controller") as null|anything in controller_choices
|
||||
var/datum/controller/controller = controllers[controller_string]
|
||||
|
||||
if (!istype(controller))
|
||||
return
|
||||
debug_variables(controller)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Restart Failsafe Controller")
|
||||
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
|
||||
/datum/config_entry/flag/announce_admin_login
|
||||
|
||||
/datum/config_entry/string/centcom_ban_db // URL for the CentCom Galactic Ban DB API
|
||||
/datum/config_entry/string/centcom_ban_db // URL for the CentCom Galactic Ban DB API
|
||||
|
||||
/datum/config_entry/string/centcom_source_whitelist
|
||||
|
||||
/datum/config_entry/flag/autoadmin // if autoadmin is enabled
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
@@ -333,3 +333,12 @@
|
||||
config_entry_value = 0.333
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/hard_deletes_overrun_threshold
|
||||
integer = FALSE
|
||||
min_val = 0
|
||||
default = 0.5
|
||||
|
||||
/datum/config_entry/number/hard_deletes_overrun_limit
|
||||
default = 0
|
||||
min_val = 0
|
||||
|
||||
@@ -15,7 +15,7 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
|
||||
var/defcon = 5
|
||||
//the world.time of the last check, so the mc can restart US if we hang.
|
||||
// (Real friends look out for *eachother*)
|
||||
// (Real friends look out for *eachother*)
|
||||
var/lasttick = 0
|
||||
|
||||
// Track the MC iteration to make sure its still on track.
|
||||
@@ -33,6 +33,22 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
/datum/controller/failsafe/Initialize()
|
||||
set waitfor = FALSE
|
||||
Failsafe.Loop()
|
||||
if (!Master || defcon == 0) //Master is gone/not responding and Failsafe just exited its loop
|
||||
defcon = 3 //Reset defcon level as its used inside the emergency loop
|
||||
while (defcon > 0)
|
||||
var/recovery_result = emergency_loop()
|
||||
if (recovery_result == 1) //Exit emergency loop and delete self if it was able to recover MC
|
||||
break
|
||||
else if (defcon == 1) //Exit Failsafe if we weren't able to recover the MC in the last stage
|
||||
log_game("FailSafe: Failed to recover MC while in emergency state. Failsafe exiting.")
|
||||
message_admins(span_boldannounce("Failsafe failed criticaly while trying to recreate broken MC. Please manually fix the MC or reboot the server. Failsafe exiting now."))
|
||||
message_admins(span_boldannounce("You can try manually calling these two procs:."))
|
||||
message_admins(span_boldannounce("/proc/recover_all_SS_and_recreate_master: Most stuff should still function but expect instability/runtimes/broken stuff."))
|
||||
message_admins(span_boldannounce("/proc/delete_all_SS_and_recreate_master: Most stuff will be broken but basic stuff like movement and chat should still work."))
|
||||
else if (recovery_result == -1) //Failed to recreate MC
|
||||
defcon--
|
||||
sleep(initial(processing_interval)) //Wait a bit until the next try
|
||||
|
||||
if(!QDELETED(src))
|
||||
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
|
||||
|
||||
@@ -45,42 +61,56 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
while(running)
|
||||
lasttick = world.time
|
||||
if(!Master)
|
||||
// Replace the missing Master! This should never, ever happen.
|
||||
new /datum/controller/master()
|
||||
// Break out of the main loop so we go into emergency state
|
||||
break
|
||||
// Only poke it if overrides are not in effect.
|
||||
if(processing_interval > 0)
|
||||
if(Master.processing && Master.iteration)
|
||||
if (defcon > 1 && (!Master.stack_end_detector || !Master.stack_end_detector.check()))
|
||||
|
||||
to_chat(GLOB.admins, span_boldannounce("ERROR: The Master Controller code stack has exited unexpectedly, Restarting..."))
|
||||
defcon = 0
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
master_iteration = 0
|
||||
to_chat(GLOB.admins, span_adminnotice("MC restarted successfully"))
|
||||
else if(rtn < 0)
|
||||
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
|
||||
to_chat(GLOB.admins, span_boldannounce("ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying."))
|
||||
// Check if processing is done yet.
|
||||
if(Master.iteration == master_iteration)
|
||||
switch(defcon)
|
||||
if(4,5)
|
||||
--defcon
|
||||
if(3)
|
||||
message_admins("<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(2)
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(1)
|
||||
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...</span>")
|
||||
if(3)
|
||||
message_admins(span_adminnotice("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks."))
|
||||
--defcon
|
||||
|
||||
if(2)
|
||||
to_chat(GLOB.admins, span_boldannounce("Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks."))
|
||||
--defcon
|
||||
|
||||
if(1)
|
||||
to_chat(GLOB.admins, span_boldannounce("Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting..."))
|
||||
--defcon
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
to_chat(GLOB.admins, span_adminnotice("MC restarted successfully"))
|
||||
else if(rtn < 0)
|
||||
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
|
||||
to_chat(GLOB.admins, span_boldannounce("ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying."))
|
||||
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
|
||||
//no need to handle that specially when defcon 0 can handle it
|
||||
|
||||
if(0) //DEFCON 0! (mc failed to restart)
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
to_chat(GLOB.admins, span_adminnotice("MC restarted successfully"))
|
||||
else
|
||||
defcon = min(defcon + 1,5)
|
||||
master_iteration = Master.iteration
|
||||
@@ -92,6 +122,57 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
defcon = 5
|
||||
sleep(initial(processing_interval))
|
||||
|
||||
//Emergency loop used when Master got deleted or the main loop exited while Defcon == 0
|
||||
//Loop is driven externally so runtimes only cancel the current recovery attempt
|
||||
/datum/controller/failsafe/proc/emergency_loop()
|
||||
//The code in this proc should be kept as simple as possible, anything complicated like to_chat might rely on master existing and runtime
|
||||
//The goal should always be to get a new Master up and running before anything else
|
||||
. = -1
|
||||
switch (defcon) //The lower defcon goes the harder we try to fix the MC
|
||||
if (2 to 3) //Try to normally recreate the MC two times
|
||||
. = Recreate_MC()
|
||||
if (1) //Delete the old MC first so we don't transfer any info, in case that caused any issues
|
||||
del(Master)
|
||||
. = Recreate_MC()
|
||||
|
||||
if (. == 1) //We were able to create a new master
|
||||
master_iteration = 0
|
||||
SSticker.Recover(); //Recover the ticket system so the Masters runlevel gets set
|
||||
Master.Initialize(10, FALSE, TRUE) //Need to manually start the MC, normally world.new would do this
|
||||
to_chat(GLOB.admins, span_adminnotice("Failsafe recovered MC while in emergency state [defcon_pretty()]"))
|
||||
else
|
||||
log_game("FailSafe: Failsafe in emergency state and was unable to recreate MC while in defcon state [defcon_pretty()].")
|
||||
message_admins(span_boldannounce("Failsafe in emergency state and master down, trying to recreate MC while in defcon level [defcon_pretty()] failed."))
|
||||
|
||||
///Recreate all SSs which will still cause data survive due to Recover(), the new Master will then find and take them from global.vars
|
||||
/proc/recover_all_SS_and_recreate_master()
|
||||
del(Master)
|
||||
var/list/subsytem_types = subtypesof(/datum/controller/subsystem)
|
||||
sortTim(subsytem_types, /proc/cmp_subsystem_init)
|
||||
for(var/I in subsytem_types)
|
||||
new I
|
||||
. = Recreate_MC()
|
||||
if (. == 1) //We were able to create a new master
|
||||
SSticker.Recover(); //Recover the ticket system so the Masters runlevel gets set
|
||||
Master.Initialize(10, FALSE, TRUE) //Need to manually start the MC, normally world.new would do this
|
||||
to_chat(GLOB.admins, span_adminnotice("MC successfully recreated after recovering all subsystems!"))
|
||||
else
|
||||
message_admins(span_boldannounce("Failed to create new MC!"))
|
||||
|
||||
///Delete all existing SS to basically start over
|
||||
/proc/delete_all_SS_and_recreate_master()
|
||||
del(Master)
|
||||
for(var/global_var in global.vars)
|
||||
if (istype(global.vars[global_var], /datum/controller/subsystem))
|
||||
del(global.vars[global_var])
|
||||
. = Recreate_MC()
|
||||
if (. == 1) //We were able to create a new master
|
||||
SSticker.Recover(); //Recover the ticket system so the Masters runlevel gets set
|
||||
Master.Initialize(10, FALSE, TRUE) //Need to manually start the MC, normally world.new would do this
|
||||
to_chat(GLOB.admins, span_adminnotice("MC successfully recreated after deleting and recreating all subsystems!"))
|
||||
else
|
||||
message_admins(span_boldannounce("Failed to create new MC!"))
|
||||
|
||||
/datum/controller/failsafe/proc/defcon_pretty()
|
||||
return defcon
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
|
||||
|
||||
var/datum/controller/exclude_these = new
|
||||
gvars_datum_in_built_vars = exclude_these.vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order))
|
||||
QDEL_IN(exclude_these, 0) //signal logging isn't ready
|
||||
QDEL_IN(exclude_these, 0) //signal logging isn't ready
|
||||
|
||||
log_world("[vars.len - gvars_datum_in_built_vars.len] global variables")
|
||||
|
||||
|
||||
+59
-57
@@ -18,15 +18,17 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
/datum/controller/master
|
||||
name = "Master"
|
||||
|
||||
// Are we processing (higher values increase the processing delay by n ticks)
|
||||
/// Are we processing (higher values increase the processing delay by n ticks)
|
||||
var/processing = TRUE
|
||||
// How many times have we ran
|
||||
/// How many times have we ran
|
||||
var/iteration = 0
|
||||
/// Stack end detector to detect stack overflows that kill the mc's main loop
|
||||
var/datum/stack_end_detector/stack_end_detector
|
||||
|
||||
// world.time of last fire, for tracking lag outside of the mc
|
||||
/// world.time of last fire, for tracking lag outside of the mc
|
||||
var/last_run
|
||||
|
||||
// List of subsystems to process().
|
||||
/// List of subsystems to process().
|
||||
var/list/subsystems
|
||||
|
||||
// Vars for keeping track of tick drift.
|
||||
@@ -34,25 +36,27 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/init_time
|
||||
var/tickdrift = 0
|
||||
|
||||
/// How long is the MC sleeping between runs, read only (set by Loop() based off of anti-tick-contention heuristics)
|
||||
var/sleep_delta = 1
|
||||
|
||||
///Only run ticker subsystems for the next n ticks.
|
||||
/// Only run ticker subsystems for the next n ticks.
|
||||
var/skip_ticks = 0
|
||||
|
||||
/// makes the mc main loop runtime
|
||||
var/make_runtime = FALSE
|
||||
|
||||
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
|
||||
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
|
||||
|
||||
// The type of the last subsystem to be process()'d.
|
||||
/// The type of the last subsystem to be fire()'d.
|
||||
var/last_type_processed
|
||||
|
||||
var/datum/controller/subsystem/queue_head //Start of queue linked list
|
||||
var/datum/controller/subsystem/queue_tail //End of queue linked list (used for appending to the list)
|
||||
var/datum/controller/subsystem/queue_head //!Start of queue linked list
|
||||
var/datum/controller/subsystem/queue_tail //!End of queue linked list (used for appending to the list)
|
||||
var/queue_priority_count = 0 //Running total so that we don't have to loop thru the queue each run to split up the tick
|
||||
var/queue_priority_count_bg = 0 //Same, but for background subsystems
|
||||
var/map_loading = FALSE //Are we loading in a new map?
|
||||
var/map_loading = FALSE //!Are we loading in a new map?
|
||||
|
||||
var/current_runlevel //for scheduling different subsystems for different stages of the round
|
||||
var/current_runlevel //!for scheduling different subsystems for different stages of the round
|
||||
var/sleep_offline_after_initializations = TRUE
|
||||
|
||||
var/static/restart_clear = 0
|
||||
@@ -61,8 +65,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
var/static/random_seed
|
||||
|
||||
//current tick limit, assigned before running a subsystem.
|
||||
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
|
||||
///current tick limit, assigned before running a subsystem.
|
||||
///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
|
||||
|
||||
/datum/controller/master/New()
|
||||
@@ -81,15 +85,27 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/list/_subsystems = list()
|
||||
subsystems = _subsystems
|
||||
if (Master != src)
|
||||
if (istype(Master))
|
||||
if (istype(Master)) //If there is an existing MC take over his stuff and delete it
|
||||
Recover()
|
||||
qdel(Master)
|
||||
Master = src
|
||||
else
|
||||
//Code used for first master on game boot or if existing master got deleted
|
||||
Master = src
|
||||
var/list/subsytem_types = subtypesof(/datum/controller/subsystem)
|
||||
sortTim(subsytem_types, /proc/cmp_subsystem_init)
|
||||
//Find any abandoned subsystem from the previous master (if there was any)
|
||||
var/list/existing_subsystems = list()
|
||||
for(var/global_var in global.vars)
|
||||
if (istype(global.vars[global_var], /datum/controller/subsystem))
|
||||
existing_subsystems += global.vars[global_var]
|
||||
//Either init a new SS or if an existing one was found use that
|
||||
for(var/I in subsytem_types)
|
||||
_subsystems += new I
|
||||
Master = src
|
||||
var/datum/controller/subsystem/existing_subsystem = locate(I) in existing_subsystems
|
||||
if (istype(existing_subsystem))
|
||||
_subsystems += existing_subsystem
|
||||
else
|
||||
_subsystems += new I
|
||||
|
||||
if(!GLOB)
|
||||
new /datum/controller/global_vars
|
||||
@@ -109,7 +125,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
log_world("Shutdown complete")
|
||||
|
||||
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
|
||||
// -1 if we encountered a runtime trying to recreate it
|
||||
// -1 if we encountered a runtime trying to recreate it
|
||||
/proc/Recreate_MC()
|
||||
. = -1 //so if we runtime, things know we failed
|
||||
if (world.time < Master.restart_timeout)
|
||||
@@ -120,7 +136,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/delay = 50 * ++Master.restart_count
|
||||
Master.restart_timeout = world.time + delay
|
||||
Master.restart_clear = world.time + (delay * 2)
|
||||
Master.processing = FALSE //stop ticking this one
|
||||
if (Master) //Can only do this if master hasn't been deleted
|
||||
Master.processing = FALSE //stop ticking this one
|
||||
try
|
||||
new/datum/controller/master()
|
||||
catch
|
||||
@@ -156,22 +173,22 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined."
|
||||
BadBoy.flags |= SS_NO_FIRE
|
||||
if(msg)
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>[msg]</span>")
|
||||
to_chat(GLOB.admins, span_boldannounce("[msg]"))
|
||||
log_world(msg)
|
||||
|
||||
if (istype(Master.subsystems))
|
||||
if(FireHim)
|
||||
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
|
||||
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
|
||||
subsystems = Master.subsystems
|
||||
current_runlevel = Master.current_runlevel
|
||||
StartProcessing(10)
|
||||
else
|
||||
to_chat(world, "<span class='boldannounce'>The Master Controller is having some issues, we will need to re-initialize EVERYTHING</span>")
|
||||
to_chat(world, span_boldannounce("The Master Controller is having some issues, we will need to re-initialize EVERYTHING"))
|
||||
Initialize(20, TRUE)
|
||||
|
||||
|
||||
// Please don't stuff random bullshit here,
|
||||
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
|
||||
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
|
||||
/datum/controller/master/Initialize(delay, init_sss, tgs_prime)
|
||||
set waitfor = 0
|
||||
|
||||
@@ -181,7 +198,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
if(init_sss)
|
||||
init_subtypes(/datum/controller/subsystem, subsystems)
|
||||
|
||||
to_chat(world, "<span class='boldannounce'>Initializing subsystems...</span>")
|
||||
to_chat(world, span_boldannounce("Initializing subsystems..."))
|
||||
|
||||
// Sort subsystems by init_order, so they initialize in the correct order.
|
||||
sortTim(subsystems, /proc/cmp_subsystem_init)
|
||||
@@ -190,7 +207,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
// Initialize subsystems.
|
||||
current_ticklimit = CONFIG_GET(number/tick_limit_mc_init)
|
||||
for (var/datum/controller/subsystem/SS in subsystems)
|
||||
if (SS.flags & SS_NO_INIT)
|
||||
if (SS.flags & SS_NO_INIT || SS.initialized) //Don't init SSs with the correspondig flag or if they already are initialzized
|
||||
continue
|
||||
SS.Initialize(REALTIMEOFDAY)
|
||||
CHECK_TICK
|
||||
@@ -198,7 +215,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
||||
|
||||
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
|
||||
to_chat(world, "<span class='boldannounce'>[msg]</span>")
|
||||
to_chat(world, span_boldannounce("[msg]"))
|
||||
log_world(msg)
|
||||
|
||||
if (!current_runlevel)
|
||||
@@ -255,11 +272,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
/datum/controller/master/proc/Loop()
|
||||
. = -1
|
||||
//Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because
|
||||
// local vars rock
|
||||
// local vars rock
|
||||
|
||||
//all this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed)
|
||||
var/list/tickersubsystems = list()
|
||||
var/list/runlevel_sorted_subsystems = list(list()) //ensure we always have at least one runlevel
|
||||
var/list/runlevel_sorted_subsystems = list(list()) //ensure we always have at least one runlevel
|
||||
var/timer = world.time
|
||||
for (var/thing in subsystems)
|
||||
var/datum/controller/subsystem/SS = thing
|
||||
@@ -305,8 +322,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/error_level = 0
|
||||
var/sleep_delta = 1
|
||||
var/list/subsystems_to_check
|
||||
//the actual loop.
|
||||
|
||||
//setup the stack overflow detector
|
||||
stack_end_detector = new()
|
||||
var/datum/stack_canary/canary = stack_end_detector.prime_canary()
|
||||
canary.use_variable()
|
||||
//the actual loop.
|
||||
while (1)
|
||||
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
|
||||
var/starting_tick_usage = TICK_USAGE
|
||||
@@ -317,7 +338,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
//Anti-tick-contention heuristics:
|
||||
//if there are mutiple sleeping procs running before us hogging the cpu, we have to run later.
|
||||
// (because sleeps are processed in the order received, longer sleeps are more likely to run first)
|
||||
// (because sleeps are processed in the order received, longer sleeps are more likely to run first)
|
||||
if (starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit.
|
||||
sleep_delta *= 2
|
||||
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
|
||||
@@ -423,6 +444,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
continue
|
||||
if ((SS_flags & (SS_TICKER|SS_KEEP_TIMING)) == SS_KEEP_TIMING && SS.last_fire + (SS.wait * 0.75) > world.time)
|
||||
continue
|
||||
if (SS.postponed_fires >= 1)
|
||||
SS.postponed_fires--
|
||||
SS.update_nextfire()
|
||||
continue
|
||||
SS.enqueue()
|
||||
. = 1
|
||||
|
||||
@@ -439,12 +464,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/tick_precentage
|
||||
var/tick_remaining
|
||||
var/ran = TRUE //this is right
|
||||
var/ran_non_ticker = FALSE
|
||||
var/bg_calc //have we swtiched current_tick_budget to background mode yet?
|
||||
var/tick_usage
|
||||
|
||||
//keep running while we have stuff to run and we haven't gone over a tick
|
||||
// this is so subsystems paused eariler can use tick time that later subsystems never used
|
||||
// this is so subsystems paused eariler can use tick time that later subsystems never used
|
||||
while (ran && queue_head && TICK_USAGE < TICK_LIMIT_MC)
|
||||
ran = FALSE
|
||||
bg_calc = FALSE
|
||||
@@ -459,20 +483,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
if (!(queue_node_flags & SS_TICKER) && skip_ticks)
|
||||
queue_node = queue_node.queue_next
|
||||
continue
|
||||
//super special case, subsystems where we can't make them pause mid way through
|
||||
//if we can't run them this tick (without going over a tick)
|
||||
//we bump up their priority and attempt to run them next tick
|
||||
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
|
||||
// in those cases, so we just let them run)
|
||||
if (queue_node_flags & SS_NO_TICK_CHECK)
|
||||
if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
|
||||
if (!(queue_node_flags & SS_BACKGROUND))
|
||||
queue_node.queued_priority += queue_priority_count * 0.1
|
||||
queue_priority_count -= queue_node_priority
|
||||
queue_priority_count += queue_node.queued_priority
|
||||
current_tick_budget -= queue_node_priority
|
||||
queue_node = queue_node.queue_next
|
||||
continue
|
||||
|
||||
if (!bg_calc && (queue_node_flags & SS_BACKGROUND))
|
||||
current_tick_budget = queue_priority_count_bg
|
||||
@@ -481,7 +491,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
tick_remaining = TICK_LIMIT_RUNNING - TICK_USAGE
|
||||
|
||||
if (current_tick_budget > 0 && queue_node_priority > 0)
|
||||
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
|
||||
//Give the subsystem a precentage of the remaining tick based on the remaning priority
|
||||
tick_precentage = tick_remaining * (queue_node_priority / current_tick_budget)
|
||||
else
|
||||
tick_precentage = tick_remaining
|
||||
|
||||
@@ -489,8 +500,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
current_ticklimit = round(TICK_USAGE + tick_precentage)
|
||||
|
||||
if (!(queue_node_flags & SS_TICKER))
|
||||
ran_non_ticker = TRUE
|
||||
ran = TRUE
|
||||
|
||||
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
|
||||
@@ -535,14 +544,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
queue_node.last_fire = world.time
|
||||
queue_node.times_fired++
|
||||
|
||||
if (queue_node_flags & SS_TICKER)
|
||||
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
|
||||
else if (queue_node_flags & SS_POST_FIRE_TIMING)
|
||||
queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
|
||||
else if (queue_node_flags & SS_KEEP_TIMING)
|
||||
queue_node.next_fire += queue_node.wait
|
||||
else
|
||||
queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
|
||||
queue_node.update_nextfire()
|
||||
|
||||
queue_node.queued_time = 0
|
||||
|
||||
@@ -554,7 +556,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
. = 1
|
||||
|
||||
//resets the queue, and all subsystems, while filtering out the subsystem lists
|
||||
// called if any mc's queue procs runtime or exit improperly.
|
||||
// called if any mc's queue procs runtime or exit improperly.
|
||||
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/runlevel_SS)
|
||||
. = 0
|
||||
log_world("MC: SoftReset called, resetting MC queue state.")
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
var/initialized = FALSE
|
||||
|
||||
/// Set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later
|
||||
/// use the [SS_NO_FIRE] flag instead for systems that never fire to keep it from even being added to list that is checked every tick
|
||||
/// use the [SS_NO_FIRE] flag instead for systems that never fire to keep it from even being added to list that is checked every tick
|
||||
var/can_fire = TRUE
|
||||
|
||||
///Bitmap of what game states can this subsystem fire at. See [RUNLEVELS_DEFAULT] for more details.
|
||||
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
|
||||
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
|
||||
|
||||
/*
|
||||
* The following variables are managed by the MC and should not be modified directly.
|
||||
@@ -69,6 +69,9 @@
|
||||
/// Tracks the amount of completed runs for the subsystem
|
||||
var/times_fired = 0
|
||||
|
||||
/// How many fires have we been requested to postpone
|
||||
var/postponed_fires = 0
|
||||
|
||||
/// Time the subsystem entered the queue, (for timing and priority reasons)
|
||||
var/queued_time = 0
|
||||
|
||||
@@ -122,12 +125,38 @@
|
||||
dequeue()
|
||||
can_fire = 0
|
||||
flags |= SS_NO_FIRE
|
||||
Master.subsystems -= src
|
||||
if (Master)
|
||||
Master.subsystems -= src
|
||||
return ..()
|
||||
|
||||
|
||||
/** Update next_fire for the next run.
|
||||
* reset_time (bool) - Ignore things that would normally alter the next fire, like tick_overrun, and last_fire. (also resets postpone)
|
||||
*/
|
||||
/datum/controller/subsystem/proc/update_nextfire(reset_time = FALSE)
|
||||
var/queue_node_flags = flags
|
||||
|
||||
if (reset_time)
|
||||
postponed_fires = 0
|
||||
if (queue_node_flags & SS_TICKER)
|
||||
next_fire = world.time + (world.tick_lag * wait)
|
||||
else
|
||||
next_fire = world.time + wait
|
||||
return
|
||||
|
||||
if (queue_node_flags & SS_TICKER)
|
||||
next_fire = world.time + (world.tick_lag * wait)
|
||||
else if (queue_node_flags & SS_POST_FIRE_TIMING)
|
||||
next_fire = world.time + wait + (world.tick_lag * (tick_overrun/100))
|
||||
else if (queue_node_flags & SS_KEEP_TIMING)
|
||||
next_fire += wait
|
||||
else
|
||||
next_fire = queued_time + wait + (world.tick_lag * (tick_overrun/100))
|
||||
|
||||
|
||||
//Queue it to run.
|
||||
// (we loop thru a linked list until we get to the end or find the right point)
|
||||
// (this lets us sort our run order correctly without having to re-sort the entire already sorted list)
|
||||
// (we loop thru a linked list until we get to the end or find the right point)
|
||||
// (this lets us sort our run order correctly without having to re-sort the entire already sorted list)
|
||||
/datum/controller/subsystem/proc/enqueue()
|
||||
var/SS_priority = priority
|
||||
var/SS_flags = flags
|
||||
@@ -191,9 +220,9 @@
|
||||
queue_next.queue_prev = queue_prev
|
||||
if (queue_prev)
|
||||
queue_prev.queue_next = queue_next
|
||||
if (src == Master.queue_tail)
|
||||
if (Master && (src == Master.queue_tail))
|
||||
Master.queue_tail = queue_prev
|
||||
if (src == Master.queue_head)
|
||||
if (Master && (src == Master.queue_head))
|
||||
Master.queue_head = queue_next
|
||||
queued_time = 0
|
||||
if (state == SS_QUEUED)
|
||||
@@ -217,10 +246,11 @@
|
||||
//used to initialize the subsystem AFTER the map has loaded
|
||||
/datum/controller/subsystem/Initialize(start_timeofday)
|
||||
initialized = TRUE
|
||||
// SEND_SIGNAL(src, COMSIG_SUBSYSTEM_POST_INITIALIZE, start_timeofday)
|
||||
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
||||
var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!"
|
||||
to_chat(world, "<span class='boldannounce'>[msg]</span>")
|
||||
log_subsystem("INIT", msg)
|
||||
to_chat(world, span_boldannounce("[msg]"))
|
||||
log_subsystem(msg)
|
||||
return time
|
||||
|
||||
/datum/controller/subsystem/stat_entry(msg)
|
||||
@@ -243,11 +273,10 @@
|
||||
if (SS_IDLE)
|
||||
. = " "
|
||||
|
||||
//could be used to postpone a costly subsystem for (default one) var/cycles, cycles
|
||||
//for instance, during cpu intensive operations like explosions
|
||||
/// Causes the next "cycle" fires to be missed. Effect is accumulative but can reset by calling update_nextfire(reset_time = TRUE)
|
||||
/datum/controller/subsystem/proc/postpone(cycles = 1)
|
||||
if(next_fire - world.time < wait)
|
||||
next_fire += (wait*cycles)
|
||||
if (can_fire && cycles >= 1)
|
||||
postponed_fires += cycles
|
||||
|
||||
//usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
|
||||
//should attempt to salvage what it can from the old instance of subsystem
|
||||
@@ -258,7 +287,7 @@
|
||||
if (NAMEOF(src, can_fire))
|
||||
//this is so the subsystem doesn't rapid fire to make up missed ticks causing more lag
|
||||
if (var_value)
|
||||
next_fire = world.time + wait
|
||||
update_nextfire(reset_time = TRUE)
|
||||
if (NAMEOF(src, queued_priority)) //editing this breaks things.
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
@@ -30,6 +30,7 @@ SUBSYSTEM_DEF(air)
|
||||
var/list/networks = list()
|
||||
var/list/pipenets_needing_rebuilt = list()
|
||||
var/list/deferred_airs = list()
|
||||
var/cur_deferred_airs = 0
|
||||
var/max_deferred_airs = 0
|
||||
var/list/obj/machinery/atmos_machinery = list()
|
||||
var/list/obj/machinery/atmos_air_machinery = list()
|
||||
@@ -54,14 +55,22 @@ SUBSYSTEM_DEF(air)
|
||||
var/equalize_turf_limit = 10
|
||||
// Max number of turfs to look for a space turf, and max number of turfs that will be decompressed.
|
||||
var/equalize_hard_turf_limit = 2000
|
||||
// Whether equalization should be enabled at all.
|
||||
// Whether equalization is enabled. Can be disabled for performance reasons.
|
||||
var/equalize_enabled = TRUE
|
||||
// Whether equalization should be enabled.
|
||||
var/should_do_equalization = TRUE
|
||||
// When above 0, won't equalize; performance handling
|
||||
var/eq_cooldown = 0
|
||||
// Whether turf-to-turf heat exchanging should be enabled.
|
||||
var/heat_enabled = FALSE
|
||||
// Max number of times process_turfs will share in a tick.
|
||||
var/share_max_steps = 3
|
||||
// Target for share_max_steps; can go below this, if it determines the thread is taking too long.
|
||||
var/share_max_steps_target = 3
|
||||
// Excited group processing will try to equalize groups with total pressure difference less than this amount.
|
||||
var/excited_group_pressure_goal = 1
|
||||
// Target for excited_group_pressure_goal; can go below this, if it determines the thread is taking too long.
|
||||
var/excited_group_pressure_goal_target = 1
|
||||
// If this is set to 0, monstermos won't process planet atmos
|
||||
var/planet_equalize_enabled = 0
|
||||
|
||||
@@ -221,6 +230,7 @@ SUBSYSTEM_DEF(air)
|
||||
// This also happens to do all the commented out stuff below, all in a single separate thread. This is mostly so that the
|
||||
// waiting is consistent.
|
||||
if(currentpart == SSAIR_ACTIVETURFS)
|
||||
run_delay_heuristics()
|
||||
timer = TICK_USAGE_REAL
|
||||
process_turfs(resumed)
|
||||
cost_turfs = MC_AVERAGE(cost_turfs, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
||||
@@ -278,7 +288,8 @@ SUBSYSTEM_DEF(air)
|
||||
pipenets_needing_rebuilt += atmos_machine
|
||||
|
||||
/datum/controller/subsystem/air/proc/process_deferred_airs(resumed = 0)
|
||||
max_deferred_airs = max(deferred_airs.len,max_deferred_airs)
|
||||
cur_deferred_airs = deferred_airs.len
|
||||
max_deferred_airs = max(cur_deferred_airs,max_deferred_airs)
|
||||
while(deferred_airs.len)
|
||||
var/list/cur_op = deferred_airs[deferred_airs.len]
|
||||
deferred_airs.len--
|
||||
@@ -378,6 +389,24 @@ SUBSYSTEM_DEF(air)
|
||||
return
|
||||
*/
|
||||
|
||||
/datum/controller/subsystem/air/proc/run_delay_heuristics()
|
||||
if(!equalize_enabled)
|
||||
cost_equalize = 0
|
||||
if(should_do_equalization)
|
||||
eq_cooldown--
|
||||
if(eq_cooldown <= 0)
|
||||
equalize_enabled = TRUE
|
||||
var/total_thread_time = cost_turfs + cost_equalize + cost_groups + cost_post_process
|
||||
if(total_thread_time)
|
||||
var/wait_ms = wait * 100
|
||||
var/delay_threshold = 1-(total_thread_time/wait_ms + cur_deferred_airs / 50)
|
||||
share_max_steps = max(1,round(share_max_steps_target * delay_threshold, 1))
|
||||
eq_cooldown += (1-delay_threshold) * (cost_equalize / total_thread_time) * 2
|
||||
if(eq_cooldown > 0.5)
|
||||
equalize_enabled = FALSE
|
||||
excited_group_pressure_goal = max(0,excited_group_pressure_goal_target * (1 - delay_threshold))
|
||||
|
||||
|
||||
/datum/controller/subsystem/air/proc/process_turfs(resumed = 0)
|
||||
if(process_turfs_auxtools(resumed,TICK_REMAINING_MS))
|
||||
pause()
|
||||
@@ -402,7 +431,7 @@ SUBSYSTEM_DEF(air)
|
||||
pause()
|
||||
|
||||
/datum/controller/subsystem/air/proc/finish_turf_processing(resumed = 0)
|
||||
if(finish_turf_processing_auxtools(TICK_REMAINING_MS))
|
||||
if(finish_turf_processing_auxtools(TICK_REMAINING_MS) || thread_running())
|
||||
pause()
|
||||
|
||||
/datum/controller/subsystem/air/proc/post_process_turfs(resumed = 0)
|
||||
|
||||
@@ -29,37 +29,35 @@ SUBSYSTEM_DEF(garbage)
|
||||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
|
||||
init_order = INIT_ORDER_GARBAGE
|
||||
|
||||
var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
|
||||
var/list/collection_timeout = list(GC_FILTER_QUEUE, GC_DEL_QUEUE) // 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
|
||||
var/gcedlasttick = 0 // number of things that gc'ed last tick
|
||||
var/delslasttick = 0 // number of del()'s we've done this tick
|
||||
var/gcedlasttick = 0 // number of things that gc'ed last tick
|
||||
var/totaldels = 0
|
||||
var/totalgcs = 0
|
||||
|
||||
var/highest_del_time = 0
|
||||
var/highest_del_tickusage = 0
|
||||
var/highest_del_ms = 0
|
||||
var/highest_del_type_string = ""
|
||||
|
||||
var/list/pass_counts
|
||||
var/list/fail_counts
|
||||
|
||||
var/list/items = list() // Holds our qdel_item statistics datums
|
||||
var/list/items = list() // Holds our qdel_item statistics datums
|
||||
|
||||
//Queue
|
||||
var/list/queues
|
||||
#ifdef LEGACY_REFERENCE_TRACKING
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/list/reference_find_on_fail = list()
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
//Should we save found refs. Used for unit testing
|
||||
var/should_save_refs = FALSE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/datum/controller/subsystem/garbage/PreInit()
|
||||
queues = new(GC_QUEUE_COUNT)
|
||||
pass_counts = new(GC_QUEUE_COUNT)
|
||||
fail_counts = new(GC_QUEUE_COUNT)
|
||||
for(var/i in 1 to GC_QUEUE_COUNT)
|
||||
queues[i] = list()
|
||||
pass_counts[i] = 0
|
||||
fail_counts[i] = 0
|
||||
InitQueues()
|
||||
|
||||
/datum/controller/subsystem/garbage/stat_entry(msg)
|
||||
var/list/counts = list()
|
||||
@@ -90,13 +88,18 @@ SUBSYSTEM_DEF(garbage)
|
||||
for(var/path in items)
|
||||
var/datum/qdel_item/I = items[path]
|
||||
dellog += "Path: [path]"
|
||||
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
|
||||
dellog += "\tSUSPENDED FOR LAG"
|
||||
if (I.failures)
|
||||
dellog += "\tFailures: [I.failures]"
|
||||
dellog += "\tqdel() Count: [I.qdels]"
|
||||
dellog += "\tDestroy() Cost: [I.destroy_time]ms"
|
||||
if (I.hard_deletes)
|
||||
dellog += "\tTotal Hard Deletes [I.hard_deletes]"
|
||||
dellog += "\tTotal Hard Deletes: [I.hard_deletes]"
|
||||
dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms"
|
||||
dellog += "\tHighest Time Spent Hard Deleting: [I.hard_delete_max]ms"
|
||||
if (I.hard_deletes_over_threshold)
|
||||
dellog += "\tHard Deletes Over Threshold: [I.hard_deletes_over_threshold]"
|
||||
if (I.slept_destroy)
|
||||
dellog += "\tSleeps: [I.slept_destroy]"
|
||||
if (I.no_respect_force)
|
||||
@@ -122,6 +125,15 @@ SUBSYSTEM_DEF(garbage)
|
||||
|
||||
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/InitQueues()
|
||||
if (isnull(queues)) // Only init the queues if they don't already exist, prevents overriding of recovered lists
|
||||
queues = new(GC_QUEUE_COUNT)
|
||||
pass_counts = new(GC_QUEUE_COUNT)
|
||||
fail_counts = new(GC_QUEUE_COUNT)
|
||||
for(var/i in 1 to GC_QUEUE_COUNT)
|
||||
queues[i] = list()
|
||||
pass_counts[i] = 0
|
||||
fail_counts[i] = 0
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK)
|
||||
if (level == GC_QUEUE_CHECK)
|
||||
@@ -153,7 +165,6 @@ SUBSYSTEM_DEF(garbage)
|
||||
if(GCd_at_time > cut_off_time)
|
||||
break // Everything else is newer, skip them
|
||||
count++
|
||||
|
||||
var/refID = L[2]
|
||||
var/datum/D
|
||||
D = locate(refID)
|
||||
@@ -162,8 +173,8 @@ SUBSYSTEM_DEF(garbage)
|
||||
++gcedlasttick
|
||||
++totalgcs
|
||||
pass_counts[level]++
|
||||
#ifdef LEGACY_REFERENCE_TRACKING
|
||||
reference_find_on_fail -= refID //It's deleted we don't care anymore.
|
||||
#ifdef REFERENCE_TRACKING
|
||||
reference_find_on_fail -= refID //It's deleted we don't care anymore.
|
||||
#endif
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
@@ -171,35 +182,43 @@ SUBSYSTEM_DEF(garbage)
|
||||
|
||||
// Something's still referring to the qdel'd object.
|
||||
fail_counts[level]++
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/ref_searching = FALSE
|
||||
#endif
|
||||
|
||||
switch (level)
|
||||
if (GC_QUEUE_CHECK)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
D.find_references()
|
||||
#elif defined(LEGACY_REFERENCE_TRACKING)
|
||||
if(reference_find_on_fail[refID])
|
||||
D.find_references_legacy()
|
||||
INVOKE_ASYNC(D, /datum/proc/find_references)
|
||||
ref_searching = TRUE
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
else
|
||||
D.find_references_legacy()
|
||||
INVOKE_ASYNC(D, /datum/proc/find_references)
|
||||
ref_searching = TRUE
|
||||
#endif
|
||||
reference_find_on_fail -= refID
|
||||
#endif
|
||||
var/type = D.type
|
||||
var/datum/qdel_item/I = items[type]
|
||||
#ifdef TESTING
|
||||
|
||||
log_world("## TESTING: GC: -- \ref[D] | [type] was unable to be GC'd --")
|
||||
#ifdef TESTING
|
||||
for(var/c in GLOB.admins) //Using testing() here would fill the logs with ADMIN_VV garbage
|
||||
var/client/admin = c
|
||||
if(!check_rights_for(admin, R_ADMIN))
|
||||
continue
|
||||
to_chat(admin, "## TESTING: GC: -- [ADMIN_VV(D)] | [type] was unable to be GC'd --")
|
||||
testing("GC: -- \ref[src] | [type] was unable to be GC'd --")
|
||||
#endif
|
||||
#ifdef REFERENCE_TRACKING
|
||||
GLOB.deletion_failures += D //It should no longer be bothered by the GC, manual deletion only.
|
||||
continue
|
||||
#endif
|
||||
I.failures++
|
||||
|
||||
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(ref_searching)
|
||||
return //ref searching intentionally cancels all further fires while running so things that hold references don't end up getting deleted, so we want to return here instead of continue
|
||||
#endif
|
||||
continue
|
||||
if (GC_QUEUE_HARDDELETE)
|
||||
HardDelete(D)
|
||||
if (MC_TICK_CHECK)
|
||||
@@ -208,27 +227,17 @@ SUBSYSTEM_DEF(garbage)
|
||||
|
||||
Queue(D, level+1)
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(ref_searching)
|
||||
return
|
||||
#endif
|
||||
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
if (count)
|
||||
queue.Cut(1,count+1)
|
||||
count = 0
|
||||
|
||||
#ifdef LEGACY_REFERENCE_TRACKING
|
||||
/datum/controller/subsystem/garbage/proc/add_type_to_findref(type)
|
||||
if(!ispath(type))
|
||||
return "NOT A VAILD PATH"
|
||||
reference_find_on_fail_types |= typecacheof(type)
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/remove_type_from_findref(type)
|
||||
if(!ispath(type))
|
||||
return "NOT A VALID PATH"
|
||||
reference_find_on_fail_types -= typesof(type)
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/clear_findref_types()
|
||||
reference_find_on_fail_types = list()
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK)
|
||||
if (isnull(D))
|
||||
return
|
||||
@@ -238,63 +247,66 @@ SUBSYSTEM_DEF(garbage)
|
||||
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
|
||||
var/tick = TICK_USAGE
|
||||
var/ticktime = world.time
|
||||
++delslasttick
|
||||
++totaldels
|
||||
var/type = D.type
|
||||
var/refID = "\ref[D]"
|
||||
|
||||
var/tick_usage = TICK_USAGE
|
||||
del(D)
|
||||
|
||||
tick = (TICK_USAGE-tick+((world.time-ticktime)/world.tick_lag*100))
|
||||
tick_usage = TICK_USAGE_TO_MS(tick_usage)
|
||||
|
||||
var/datum/qdel_item/I = items[type]
|
||||
|
||||
I.hard_deletes++
|
||||
I.hard_delete_time += TICK_DELTA_TO_MS(tick)
|
||||
I.hard_delete_time += tick_usage
|
||||
if (tick_usage > I.hard_delete_max)
|
||||
I.hard_delete_max = tick_usage
|
||||
if (tick_usage > highest_del_ms)
|
||||
highest_del_ms = tick_usage
|
||||
highest_del_type_string = "[type]"
|
||||
|
||||
var/time = MS2DS(tick_usage)
|
||||
|
||||
if (tick > highest_del_tickusage)
|
||||
highest_del_tickusage = tick
|
||||
time = world.timeofday - time
|
||||
if (!time && TICK_DELTA_TO_MS(tick) > 1)
|
||||
time = TICK_DELTA_TO_MS(tick)/100
|
||||
if (time > highest_del_time)
|
||||
highest_del_time = time
|
||||
if (time > 10)
|
||||
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
|
||||
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
|
||||
if (time > 0.1 SECONDS)
|
||||
postpone(time)
|
||||
var/threshold = CONFIG_GET(number/hard_deletes_overrun_threshold)
|
||||
if (threshold && (time > threshold SECONDS))
|
||||
if (!(I.qdel_flags & QDEL_ITEM_ADMINS_WARNED))
|
||||
log_game("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete)")
|
||||
message_admins("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete).")
|
||||
I.qdel_flags |= QDEL_ITEM_ADMINS_WARNED
|
||||
I.hard_deletes_over_threshold++
|
||||
var/overrun_limit = CONFIG_GET(number/hard_deletes_overrun_limit)
|
||||
if (overrun_limit && I.hard_deletes_over_threshold >= overrun_limit)
|
||||
I.qdel_flags |= QDEL_ITEM_SUSPENDED_FOR_LAG
|
||||
|
||||
/datum/controller/subsystem/garbage/Recover()
|
||||
InitQueues() //We first need to create the queues before recovering data
|
||||
if (istype(SSgarbage.queues))
|
||||
for (var/i in 1 to SSgarbage.queues.len)
|
||||
queues[i] |= SSgarbage.queues[i]
|
||||
|
||||
|
||||
/// Qdel Item: Holds statistics on each type that passes thru qdel
|
||||
/datum/qdel_item
|
||||
var/name = ""
|
||||
var/qdels = 0 //Total number of times it's passed thru qdel.
|
||||
var/destroy_time = 0 //Total amount of milliseconds spent processing this type's Destroy()
|
||||
var/failures = 0 //Times it was queued for soft deletion but failed to soft delete.
|
||||
var/hard_deletes = 0 //Different from failures because it also includes QDEL_HINT_HARDDEL deletions
|
||||
var/hard_delete_time = 0//Total amount of milliseconds spent hard deleting this type.
|
||||
var/no_respect_force = 0//Number of times it's not respected force=TRUE
|
||||
var/no_hint = 0 //Number of times it's not even bother to give a qdel hint
|
||||
var/slept_destroy = 0 //Number of times it's slept in its destroy
|
||||
var/name = "" //!Holds the type as a string for this type
|
||||
var/qdels = 0 //!Total number of times it's passed thru qdel.
|
||||
var/destroy_time = 0 //!Total amount of milliseconds spent processing this type's Destroy()
|
||||
var/failures = 0 //!Times it was queued for soft deletion but failed to soft delete.
|
||||
var/hard_deletes = 0 //!Different from failures because it also includes QDEL_HINT_HARDDEL deletions
|
||||
var/hard_delete_time = 0 //!Total amount of milliseconds spent hard deleting this type.
|
||||
var/hard_delete_max = 0 //!Highest time spent hard_deleting this in ms.
|
||||
var/hard_deletes_over_threshold = 0 //!Number of times hard deletes took longer than the configured threshold
|
||||
var/no_respect_force = 0 //!Number of times it's not respected force=TRUE
|
||||
var/no_hint = 0 //!Number of times it's not even bother to give a qdel hint
|
||||
var/slept_destroy = 0 //!Number of times it's slept in its destroy
|
||||
var/qdel_flags = 0 //!Flags related to this type's trip thru qdel.
|
||||
|
||||
/datum/qdel_item/New(mytype)
|
||||
name = "[mytype]"
|
||||
@@ -307,12 +319,12 @@ SUBSYSTEM_DEF(garbage)
|
||||
if(!istype(D))
|
||||
del(D)
|
||||
return
|
||||
|
||||
var/datum/qdel_item/I = SSgarbage.items[D.type]
|
||||
if (!I)
|
||||
I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type)
|
||||
I.qdels++
|
||||
|
||||
|
||||
if(isnull(D.gc_destroyed))
|
||||
if (SEND_SIGNAL(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted
|
||||
return
|
||||
@@ -328,12 +340,12 @@ SUBSYSTEM_DEF(garbage)
|
||||
if(!D)
|
||||
return
|
||||
switch(hint)
|
||||
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
|
||||
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
|
||||
SSgarbage.Queue(D)
|
||||
if (QDEL_HINT_IWILLGC)
|
||||
D.gc_destroyed = world.time
|
||||
return
|
||||
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
|
||||
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
|
||||
if(!force)
|
||||
D.gc_destroyed = null //clear the gc variable (important!)
|
||||
return
|
||||
@@ -350,17 +362,17 @@ SUBSYSTEM_DEF(garbage)
|
||||
I.no_respect_force++
|
||||
|
||||
SSgarbage.Queue(D)
|
||||
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete
|
||||
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete
|
||||
SSgarbage.Queue(D, GC_QUEUE_HARDDELETE)
|
||||
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
|
||||
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
|
||||
SSgarbage.HardDelete(D)
|
||||
#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.
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if (QDEL_HINT_FINDREFERENCE) //qdel will, if 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)
|
||||
D.find_references()
|
||||
if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object.
|
||||
SSgarbage.Queue(D)
|
||||
SSgarbage.reference_find_on_fail[REF(D)] = TRUE
|
||||
SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE
|
||||
#endif
|
||||
else
|
||||
#ifdef TESTING
|
||||
@@ -371,18 +383,3 @@ SUBSYSTEM_DEF(garbage)
|
||||
SSgarbage.Queue(D)
|
||||
else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
|
||||
|
||||
#ifdef TESTING
|
||||
/proc/writeDatumCount()
|
||||
var/list/datums = list()
|
||||
for(var/datum/D in world)
|
||||
datums[D.type] += 1
|
||||
for(var/datum/D)
|
||||
datums[D.type] += 1
|
||||
datums = sortTim(datums, /proc/cmp_numeric_dsc, associative = TRUE)
|
||||
if(fexists("data/DATUMCOUNT.txt"))
|
||||
fdel("data/DATUMCOUNT.txt")
|
||||
var/outfile = file("data/DATUMCOUNT.txt")
|
||||
for(var/path in datums)
|
||||
outfile << "[datums[path]]\t\t\t\t\t[path]"
|
||||
#endif
|
||||
|
||||
@@ -366,10 +366,16 @@ SUBSYSTEM_DEF(research)
|
||||
techweb_categories[I.category] = list(I.id = TRUE)
|
||||
|
||||
/datum/controller/subsystem/research/proc/techweb_node_by_id(id)
|
||||
return techweb_nodes[id] || error_node
|
||||
if(techweb_nodes[id])
|
||||
return techweb_nodes[id]
|
||||
stack_trace("Attempted to access node ID [id] which didn't exist")
|
||||
return error_node
|
||||
|
||||
/datum/controller/subsystem/research/proc/techweb_design_by_id(id)
|
||||
return techweb_designs[id] || error_design
|
||||
if(techweb_designs[id])
|
||||
return techweb_designs[id]
|
||||
stack_trace("Attempted to access design ID [id] which didn't exist")
|
||||
return error_design
|
||||
|
||||
/datum/controller/subsystem/research/proc/on_design_deletion(datum/design/D)
|
||||
for(var/i in techweb_nodes)
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
GAS_CO2=10,
|
||||
)
|
||||
restricted_gases = list(
|
||||
GAS_PLASMA=0.1,
|
||||
GAS_BZ=1.2,
|
||||
GAS_METHANE=1.0,
|
||||
GAS_BZ=0.1,
|
||||
GAS_METHYL_BROMIDE=0.1,
|
||||
)
|
||||
restricted_chance = 30
|
||||
@@ -23,7 +21,7 @@
|
||||
maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1
|
||||
|
||||
minimum_temp = BODYTEMP_COLD_DAMAGE_LIMIT + 1
|
||||
maximum_temp = 350
|
||||
maximum_temp = 320
|
||||
|
||||
/datum/atmosphere/icemoon
|
||||
id = ICEMOON_DEFAULT_ATMOS
|
||||
@@ -38,8 +36,6 @@
|
||||
GAS_CO2=10,
|
||||
)
|
||||
restricted_gases = list(
|
||||
GAS_PLASMA=0.1,
|
||||
GAS_METHANE=1.0,
|
||||
GAS_METHYL_BROMIDE=0.1,
|
||||
)
|
||||
restricted_chance = 10
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
R.update_icon()
|
||||
to_chat(user, "<span class='info'>You strengthen [R], improving its resistance against melee, bullet and laser damage.</span>")
|
||||
else
|
||||
SEND_SIGNAL(O, COMSIG_ARMOR_PLATED, amount, maxamount)
|
||||
to_chat(user, "<span class='info'>You strengthen [O], improving its resistance against melee attacks.</span>")
|
||||
|
||||
|
||||
|
||||
@@ -57,9 +57,13 @@
|
||||
*/
|
||||
var/list/cooldowns
|
||||
|
||||
#ifdef TESTING
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/running_find_references
|
||||
var/last_find_references = 0
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
///Stores info about where refs are found, used for sanity checks and testing
|
||||
var/list/found_refs
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DATUMVAR_DEBUGGING_MODE
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
Stack End Detector.
|
||||
Can detect if a given code stack has exited, used by the mc for stack overflow detection.
|
||||
|
||||
**/
|
||||
/datum/stack_end_detector
|
||||
var/datum/weakref/_WF
|
||||
var/datum/stack_canary/_canary
|
||||
|
||||
/datum/stack_end_detector/New()
|
||||
_canary = new()
|
||||
_WF = WEAKREF(_canary)
|
||||
|
||||
/** Prime the stack overflow detector.
|
||||
Store the return value of this proc call in a proc level var.
|
||||
Can only be called once.
|
||||
**/
|
||||
/datum/stack_end_detector/proc/prime_canary()
|
||||
if (!_canary)
|
||||
CRASH("Prime_canary called twice")
|
||||
. = _canary
|
||||
_canary = null
|
||||
|
||||
/// Returns true if the stack is still going. Calling before the canary has been primed also returns true
|
||||
/datum/stack_end_detector/proc/check()
|
||||
return !!_WF.resolve()
|
||||
|
||||
/// Stack canary. Will go away if the stack it was primed by is ended by byond for return or stack overflow reasons.
|
||||
/datum/stack_canary
|
||||
|
||||
/// empty proc to avoid warnings about unused variables. Call this proc on your canary in the stack it's watching.
|
||||
/datum/stack_canary/proc/use_variable()
|
||||
+11
-3
@@ -6,10 +6,12 @@
|
||||
var/body
|
||||
var/headers
|
||||
var/url
|
||||
/// If present response body will be saved to this file.
|
||||
var/output_file
|
||||
|
||||
var/_raw_response
|
||||
|
||||
/datum/http_request/proc/prepare(method, url, body = "", list/headers)
|
||||
/datum/http_request/proc/prepare(method, url, body = "", list/headers, output_file)
|
||||
if (!length(headers))
|
||||
headers = ""
|
||||
else
|
||||
@@ -19,15 +21,16 @@
|
||||
src.url = url
|
||||
src.body = body
|
||||
src.headers = headers
|
||||
src.output_file = output_file
|
||||
|
||||
/datum/http_request/proc/execute_blocking()
|
||||
_raw_response = rustg_http_request_blocking(method, url, body, headers)
|
||||
_raw_response = rustg_http_request_blocking(method, url, body, headers, build_options())
|
||||
|
||||
/datum/http_request/proc/begin_async()
|
||||
if (in_progress)
|
||||
CRASH("Attempted to re-use a request object.")
|
||||
|
||||
id = rustg_http_request_async(method, url, body, headers)
|
||||
id = rustg_http_request_async(method, url, body, headers, build_options())
|
||||
|
||||
if (isnull(text2num(id)))
|
||||
stack_trace("Proc error: [id]")
|
||||
@@ -35,6 +38,11 @@
|
||||
else
|
||||
in_progress = TRUE
|
||||
|
||||
/datum/http_request/proc/build_options()
|
||||
if(output_file)
|
||||
return json_encode(list("output_filename"=output_file,"body_filename"=null))
|
||||
return null
|
||||
|
||||
/datum/http_request/proc/is_complete()
|
||||
if (isnull(id))
|
||||
return TRUE
|
||||
|
||||
@@ -162,3 +162,20 @@
|
||||
gain_text = "<span class='notice'>You feel like munching on a can of soda.</span>"
|
||||
lose_text = "<span class='notice'>You no longer feel like you should be eating trash.</span>"
|
||||
mob_trait = TRAIT_TRASHCAN
|
||||
|
||||
/datum/quirk/colorist
|
||||
name = "Colorist"
|
||||
desc = "You like carrying around a hair dye spray to quickly apply color patterns to your hair."
|
||||
value = 0
|
||||
medical_record_text = "Patient enjoys dyeing their hair with pretty colors."
|
||||
|
||||
/datum/quirk/colorist/on_spawn()
|
||||
var/mob/living/carbon/human/H = quirk_holder
|
||||
var/obj/item/dyespray/spraycan = new(get_turf(quirk_holder))
|
||||
H.equip_to_slot(spraycan, SLOT_IN_BACKPACK)
|
||||
H.regenerate_icons()
|
||||
|
||||
/datum/quirk/colorist/post_add()
|
||||
var/mob/living/carbon/human/H = quirk_holder
|
||||
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
|
||||
to_chat(quirk_holder, "<span class='boldnotice'>You brought some extra dye with you! It's in your bag if you forgot.</span>")
|
||||
|
||||
@@ -151,14 +151,14 @@ Credit where due:
|
||||
var/datum/team/clockcult/main_clockcult
|
||||
|
||||
/datum/game_mode/clockwork_cult/pre_setup() //Gamemode and job code is pain. Have fun codediving all of that stuff, whoever works on this next - Delta
|
||||
/*var/list/errorList = list()
|
||||
var/list/errorList = list()
|
||||
var/list/reebes = SSmapping.LoadGroup(errorList, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE, silent = TRUE)
|
||||
if(errorList.len) // reebe failed to load
|
||||
message_admins("Reebe failed to load!")
|
||||
log_game("Reebe failed to load!")
|
||||
return FALSE
|
||||
for(var/datum/parsed_map/PM in reebes) //Temporarily commented because of z-level loading reliably segfaulting the server.
|
||||
PM.initTemplateBounds()*/
|
||||
PM.initTemplateBounds()
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
restricted_jobs += protected_jobs
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
@@ -275,7 +275,7 @@ Credit where due:
|
||||
ears = /obj/item/radio/headset
|
||||
gloves = /obj/item/clothing/gloves/color/yellow
|
||||
belt = /obj/item/storage/belt/utility/servant
|
||||
backpack_contents = list(/obj/item/storage/box/engineer = 1, \
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/clockwork/replica_fabricator = 1, /obj/item/stack/tile/brass/fifty = 1, /obj/item/reagent_containers/food/drinks/bottle/holyoil = 1)
|
||||
id = /obj/item/pda
|
||||
var/plasmaman //We use this to determine if we should activate internals in post_equip()
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
l_pocket = /obj/item/pinpointer/nuke/syndicate
|
||||
r_pocket = /obj/item/bikehorn
|
||||
id = /obj/item/card/id/syndicate
|
||||
backpack_contents = list(/obj/item/storage/box/syndie=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
/obj/item/kitchen/knife/combat/survival,
|
||||
/obj/item/reagent_containers/spray/waterflower/lube)
|
||||
implants = list(/obj/item/implant/sad_trombone)
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
l_pocket = /obj/item/pinpointer/nuke/syndicate
|
||||
id = /obj/item/card/id/syndicate
|
||||
belt = /obj/item/gun/ballistic/automatic/pistol
|
||||
backpack_contents = list(/obj/item/storage/box/syndie=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
/obj/item/kitchen/knife/combat/survival)
|
||||
|
||||
var/tc = 25
|
||||
@@ -173,7 +173,7 @@
|
||||
internals_slot = SLOT_R_STORE
|
||||
belt = /obj/item/storage/belt/military
|
||||
r_hand = /obj/item/gun/ballistic/automatic/shotgun/bulldog
|
||||
backpack_contents = list(/obj/item/storage/box/syndie=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
/obj/item/tank/jetpack/oxygen/harness=1,\
|
||||
/obj/item/gun/ballistic/automatic/pistol=1,\
|
||||
/obj/item/kitchen/knife/combat/survival)
|
||||
@@ -188,7 +188,7 @@
|
||||
r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
|
||||
internals_slot = SLOT_R_STORE
|
||||
belt = /obj/item/storage/belt/military
|
||||
backpack_contents = list(/obj/item/storage/box/syndie=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
/obj/item/tank/jetpack/oxygen/harness=1,\
|
||||
/obj/item/gun/ballistic/automatic/pistol=1,\
|
||||
/obj/item/kitchen/knife/combat/survival)
|
||||
|
||||
@@ -568,6 +568,12 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
if(usr.incapacitated() || !Adjacent(usr) || usr.lying)
|
||||
return
|
||||
|
||||
if(iscyborg(usr))
|
||||
var/obj/item/gripper/gripper = usr.get_active_held_item(TRUE)
|
||||
if(istype(gripper))
|
||||
gripper.pre_attack(src, usr, get_dist(src, usr))
|
||||
return
|
||||
|
||||
if(usr.get_active_held_item() == null) // Let me know if this has any problems -Yota
|
||||
usr.UnarmedAttack(src)
|
||||
|
||||
@@ -879,7 +885,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
|
||||
/obj/item/MouseEntered(location, control, params)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params)
|
||||
if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && usr.client.prefs.enable_tips && !QDELETED(src) || isobserver(usr))
|
||||
if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && usr.client.prefs.enable_tips && !QDELETED(src))
|
||||
var/timedelay = usr.client.prefs.tip_delay/100
|
||||
var/user = usr
|
||||
tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, user), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
|
||||
@@ -900,7 +906,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
remove_outline()
|
||||
|
||||
/obj/item/proc/apply_outline(colour = null)
|
||||
if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src))
|
||||
if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr))
|
||||
return
|
||||
if(usr.client)
|
||||
if(!usr.client.prefs.outline_enabled)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/obj/item/dyespray
|
||||
name = "hair dye spray"
|
||||
desc = "A spray to dye your hair any gradients you'd like."
|
||||
icon = 'icons/obj/dyespray.dmi'
|
||||
icon_state = "dyespray"
|
||||
|
||||
/obj/item/dyespray/attack_self(mob/user)
|
||||
dye(user)
|
||||
|
||||
/obj/item/dyespray/pre_attack(atom/target, mob/living/user, params)
|
||||
dye(target)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Applies a gradient and a gradient color to a mob.
|
||||
*
|
||||
* Arguments:
|
||||
* * target - The mob who we will apply the gradient and gradient color to.
|
||||
*/
|
||||
|
||||
/obj/item/dyespray/proc/dye(mob/target)
|
||||
if(!ishuman(target))
|
||||
return
|
||||
var/mob/living/carbon/human/human_target = target
|
||||
|
||||
var/new_grad_style = input(usr, "Choose a color pattern:", "Character Preference") as null|anything in GLOB.hair_gradients_list
|
||||
if(!new_grad_style)
|
||||
return
|
||||
|
||||
var/new_grad_color = input(usr, "Choose a secondary hair color:", "Character Preference","#"+human_target.grad_color) as color|null
|
||||
if(!new_grad_color)
|
||||
return
|
||||
|
||||
human_target.grad_style = new_grad_style
|
||||
human_target.grad_color = sanitize_hexcolor(new_grad_color)
|
||||
to_chat(human_target, "<span class='notice'>You start applying the hair dye...</span>")
|
||||
if(!do_after(usr, 30, target = human_target))
|
||||
return
|
||||
playsound(src, 'sound/effects/spray.ogg', 5, TRUE, 5)
|
||||
human_target.update_hair()
|
||||
@@ -184,7 +184,7 @@
|
||||
hitcost = 75 //Costs more than a standard cyborg esword
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
sharpness = SHARP_EDGED
|
||||
light_color = "#40ceff"
|
||||
light_color = LIGHT_COLOR_RED
|
||||
tool_behaviour = TOOL_SAW
|
||||
toolspeed = 0.7
|
||||
|
||||
|
||||
@@ -745,9 +745,9 @@
|
||||
Grippers oh god oh fuck
|
||||
***********************************************************************/
|
||||
|
||||
/obj/item/weapon/gripper
|
||||
/obj/item/gripper
|
||||
name = "engineering gripper"
|
||||
desc = "A simple grasping tool for interacting with various engineering related items, such as circuits, gas tanks, conveyer belts and more. Alt click to drop instead of use."
|
||||
desc = "A simple grasping tool for interacting with various engineering related items, such as circuits, gas tanks, conveyer belts and more."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "gripper"
|
||||
|
||||
@@ -776,18 +776,29 @@
|
||||
|
||||
var/obj/item/wrapped = null // Item currently being held.
|
||||
|
||||
//Used to interact with UI's of held items, such as gas tanks and airlock electronics.
|
||||
/obj/item/weapon/gripper/AltClick(mob/user)
|
||||
// Used to drop whatever's in the gripper.
|
||||
/obj/item/gripper/proc/drop_held(silent = FALSE)
|
||||
if(wrapped)
|
||||
wrapped.forceMove(get_turf(wrapped))
|
||||
to_chat(user, "<span class='notice'>You drop the [wrapped].</span>")
|
||||
if(!silent)
|
||||
to_chat(usr, "<span class='notice'>You drop the [wrapped].</span>")
|
||||
modify_appearance(wrapped, FALSE)
|
||||
wrapped = null
|
||||
return ..()
|
||||
update_appearance()
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/weapon/gripper/pre_attack(var/atom/target, var/mob/living/silicon/robot/user, proximity, params)
|
||||
/obj/item/gripper/proc/takeitem(obj/item/item, silent = FALSE)
|
||||
if(!silent)
|
||||
to_chat(usr, "<span class='notice'>You collect \the [item].</span>")
|
||||
item.loc = src
|
||||
wrapped = item
|
||||
update_appearance()
|
||||
|
||||
if(!proximity)
|
||||
return
|
||||
/obj/item/gripper/pre_attack(atom/target, mob/living/silicon/robot/user, params)
|
||||
var/proximity = get_dist(user, target)
|
||||
if(proximity > 1)
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
if(!wrapped)
|
||||
for(var/obj/item/thing in src.contents)
|
||||
@@ -795,21 +806,24 @@
|
||||
break
|
||||
|
||||
if(wrapped) //Already have an item.
|
||||
var/obj/item/item = wrapped
|
||||
drop_held(TRUE)
|
||||
//Temporary put wrapped into user so target's attackby() checks pass.
|
||||
wrapped.loc = user
|
||||
item.loc = user
|
||||
|
||||
//Pass the attack on to the target. This might delete/relocate wrapped.
|
||||
var/resolved = target.attackby(wrapped,user)
|
||||
if(!resolved && wrapped && target)
|
||||
wrapped.afterattack(target,user,1)
|
||||
var/resolved = target.attackby(item, user, params)
|
||||
if(!resolved && item && target)
|
||||
item.afterattack(target, user, proximity, params)
|
||||
//If wrapped was neither deleted nor put into target, put it back into the gripper.
|
||||
if(wrapped && user && (wrapped.loc == user))
|
||||
wrapped.loc = src
|
||||
else
|
||||
wrapped = null
|
||||
if(item && user && (item.loc == user))
|
||||
takeitem(item, TRUE)
|
||||
return
|
||||
else
|
||||
item = null
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
else if(istype(target,/obj/item))
|
||||
else if(isitem(target))
|
||||
var/obj/item/I = target
|
||||
var/grab = 0
|
||||
|
||||
@@ -824,24 +838,94 @@
|
||||
|
||||
//We can grab the item, finally.
|
||||
if(grab)
|
||||
to_chat(user, "<span class='notice'>You collect \the [I].</span>")
|
||||
I.loc = src
|
||||
wrapped = I
|
||||
takeitem(I)
|
||||
return
|
||||
else
|
||||
to_chat(user, "<span class='danger'>Your gripper cannot hold \the [target].</span>")
|
||||
|
||||
/obj/item/weapon/gripper/mining
|
||||
// Rare cases - meant to be handled by code\modules\mob\living\silicon\robot\robot.dm:584 and the weirdness of get_active_held_item() of borgs.
|
||||
/obj/item/gripper/attack_self(mob/user)
|
||||
if(wrapped)
|
||||
wrapped.attack_self(user)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
// Splitable items
|
||||
/obj/item/gripper/AltClick(mob/user)
|
||||
if(wrapped)
|
||||
wrapped.AltClick(user)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
// Even rarer cases
|
||||
/obj/item/gripper/CtrlClick(mob/user)
|
||||
if(wrapped)
|
||||
wrapped.CtrlClick(user)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
// At this point you're just kidding me, but have this one as well.
|
||||
/obj/item/gripper/CtrlShiftClick(mob/user)
|
||||
if(wrapped)
|
||||
wrapped.CtrlShiftClick(user)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
// Make it clear what we can do with it.
|
||||
/obj/item/gripper/examine(mob/user)
|
||||
. = ..()
|
||||
if(wrapped)
|
||||
. += "<span class='notice'>It is holding [icon2html(wrapped, user)] [wrapped].</span>"
|
||||
. += "<span class='notice'>Examine the little preview to examine it.</span>"
|
||||
. += "<span class='notice'>Attempting to drop the gripper will only drop [wrapped].</span>"
|
||||
|
||||
// Resets vis_contents and if holding something, add it to vis_contents.
|
||||
/obj/item/gripper/update_appearance(updates)
|
||||
. = ..()
|
||||
vis_contents = list()
|
||||
if(wrapped)
|
||||
modify_appearance(wrapped, TRUE)
|
||||
vis_contents += wrapped
|
||||
|
||||
// Generates the "minified" version of the item being held and adjust it's position.
|
||||
/obj/item/gripper/proc/modify_appearance(obj/item, minify = FALSE)
|
||||
if(minify)
|
||||
var/matrix/new_transform = new
|
||||
new_transform.Scale(0.5, 0.5)
|
||||
item.transform = new_transform
|
||||
item.pixel_x = 8
|
||||
item.pixel_y = -8
|
||||
else
|
||||
item.pixel_x = initial(pixel_x)
|
||||
item.pixel_y = initial(pixel_y)
|
||||
item.transform = new
|
||||
|
||||
// I kind of wanted the item to be held in the gripper when stored as well, but i realized "store" is just drop as well, so i'll do this for now.
|
||||
// This will handle cases where the borg runs out of power or is damaged enough so the module is forcefully stored.
|
||||
/obj/item/gripper/cyborg_unequip(mob/user)
|
||||
. = ..()
|
||||
if(wrapped)
|
||||
drop_held()
|
||||
|
||||
// Clear references on being destroyed
|
||||
/obj/item/Destroy()
|
||||
for(var/obj/item/gripper/gripper in vis_locs)
|
||||
if(gripper.wrapped == src)
|
||||
gripper.wrapped = null
|
||||
gripper.update_appearance()
|
||||
. = ..()
|
||||
|
||||
/obj/item/gripper/mining
|
||||
name = "shelter capsule deployer"
|
||||
desc = "A simple grasping tool for carrying and deploying shelter capsules. Alt click to drop instead of use."
|
||||
desc = "A simple grasping tool for carrying and deploying shelter capsules."
|
||||
icon_state = "gripper_mining"
|
||||
can_hold = list(
|
||||
/obj/item/survivalcapsule
|
||||
)
|
||||
|
||||
/obj/item/weapon/gripper/medical
|
||||
/obj/item/gripper/medical
|
||||
name = "medical gripper"
|
||||
desc = "A simple grasping tool for interacting with medical equipment, such as beakers, blood bags, chem bags and more. Alt click to drop instead of use."
|
||||
desc = "A simple grasping tool for interacting with medical equipment, such as beakers, blood bags, chem bags and more."
|
||||
icon_state = "gripper_medical"
|
||||
can_hold = list(
|
||||
/obj/item/storage/bag/bio,
|
||||
|
||||
@@ -502,6 +502,8 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \
|
||||
new /datum/stack_recipe("sterile masks box", /obj/item/storage/box/masks), \
|
||||
new /datum/stack_recipe("body bag box", /obj/item/storage/box/bodybags), \
|
||||
new /datum/stack_recipe("prescription glasses box", /obj/item/storage/box/rxglasses), \
|
||||
new /datum/stack_recipe("oxygen tank box", /obj/item/storage/box/emergencytank), \
|
||||
new /datum/stack_recipe("extended oxygen tank box", /obj/item/storage/box/engitank), \
|
||||
null, \
|
||||
new /datum/stack_recipe("disk box", /obj/item/storage/box/disks), \
|
||||
new /datum/stack_recipe("light tubes box", /obj/item/storage/box/lights/tubes), \
|
||||
|
||||
@@ -102,12 +102,22 @@
|
||||
new /obj/item/disk/nanite_program(src)
|
||||
|
||||
// Ordinary survival box
|
||||
/obj/item/storage/box/survival
|
||||
name = "survival box"
|
||||
desc = "A box with the bare essentials of ensuring the survival of you and others."
|
||||
icon_state = "internals"
|
||||
illustration = "emergencytank"
|
||||
var/mask_type = /obj/item/clothing/mask/breath
|
||||
var/internal_type = /obj/item/tank/internals/emergency_oxygen
|
||||
var/medipen_type = /obj/item/reagent_containers/hypospray/medipen
|
||||
|
||||
/obj/item/storage/box/survival/PopulateContents()
|
||||
new /obj/item/clothing/mask/breath(src)
|
||||
new /obj/item/reagent_containers/hypospray/medipen(src)
|
||||
new mask_type(src)
|
||||
if(!isnull(medipen_type))
|
||||
new medipen_type(src)
|
||||
|
||||
if(!isplasmaman(loc))
|
||||
new /obj/item/tank/internals/emergency_oxygen(src)
|
||||
new internal_type(src)
|
||||
else
|
||||
new /obj/item/tank/internals/plasmaman/belt(src)
|
||||
|
||||
@@ -115,10 +125,13 @@
|
||||
..() // we want the survival stuff too.
|
||||
new /obj/item/radio/off(src)
|
||||
|
||||
/obj/item/storage/box/survival_mining/PopulateContents()
|
||||
new /obj/item/clothing/mask/gas/explorer(src)
|
||||
// Mining survival box
|
||||
/obj/item/storage/box/survival/mining
|
||||
mask_type = /obj/item/clothing/mask/gas/explorer
|
||||
|
||||
/obj/item/storage/box/survival/mining/PopulateContents()
|
||||
..()
|
||||
new /obj/item/crowbar/red(src)
|
||||
new /obj/item/reagent_containers/hypospray/medipen(src)
|
||||
|
||||
if(!isplasmaman(loc))
|
||||
new /obj/item/tank/internals/emergency_oxygen(src)
|
||||
@@ -126,39 +139,30 @@
|
||||
new /obj/item/tank/internals/plasmaman/belt(src)
|
||||
|
||||
// Engineer survival box
|
||||
/obj/item/storage/box/engineer/PopulateContents()
|
||||
new /obj/item/clothing/mask/breath(src)
|
||||
new /obj/item/reagent_containers/hypospray/medipen(src)
|
||||
/obj/item/storage/box/survival/engineer
|
||||
name = "extended-capacity survival box"
|
||||
desc = "A box with the bare essentials of ensuring the survival of you and others. This one is labelled to contain an extended-capacity tank."
|
||||
illustration = "extendedtank"
|
||||
internal_type = /obj/item/tank/internals/emergency_oxygen/engi
|
||||
|
||||
if(!isplasmaman(loc))
|
||||
new /obj/item/tank/internals/emergency_oxygen/engi(src)
|
||||
else
|
||||
new /obj/item/tank/internals/plasmaman/belt(src)
|
||||
|
||||
/obj/item/storage/box/engineer/radio/PopulateContents()
|
||||
/obj/item/storage/box/survival/engineer/radio/PopulateContents()
|
||||
..() // we want the regular items too.
|
||||
new /obj/item/radio/off(src)
|
||||
|
||||
// Syndie survival box
|
||||
/obj/item/storage/box/syndie/PopulateContents()
|
||||
new /obj/item/clothing/mask/gas/syndicate(src)
|
||||
|
||||
if(!isplasmaman(loc))
|
||||
new /obj/item/tank/internals/emergency_oxygen/engi(src)
|
||||
else
|
||||
new /obj/item/tank/internals/plasmaman/belt(src)
|
||||
/obj/item/storage/box/survival/syndie //why is this its own thing if it's just the engi box with a syndie mask and medipen?
|
||||
name = "extended-capacity survival box"
|
||||
desc = "A box with the bare essentials of ensuring the survival of you and others. This one is labelled to contain an extended-capacity tank."
|
||||
illustration = "extendedtank"
|
||||
mask_type = /obj/item/clothing/mask/gas/syndicate
|
||||
internal_type = /obj/item/tank/internals/emergency_oxygen/engi
|
||||
medipen_type = null
|
||||
|
||||
// Security survival box
|
||||
/obj/item/storage/box/security/PopulateContents()
|
||||
new /obj/item/clothing/mask/gas/sechailer(src)
|
||||
new /obj/item/reagent_containers/hypospray/medipen(src)
|
||||
/obj/item/storage/box/survival/security
|
||||
mask_type = /obj/item/clothing/mask/gas/sechailer
|
||||
|
||||
if(!isplasmaman(loc))
|
||||
new /obj/item/tank/internals/emergency_oxygen(src)
|
||||
else
|
||||
new /obj/item/tank/internals/plasmaman/belt(src)
|
||||
|
||||
/obj/item/storage/box/security/radio/PopulateContents()
|
||||
/obj/item/storage/box/survival/security/radio/PopulateContents()
|
||||
..() // we want the regular stuff too
|
||||
new /obj/item/radio/off(src)
|
||||
|
||||
@@ -1244,6 +1248,26 @@
|
||||
icon_state = "box_pink"
|
||||
illustration = null
|
||||
|
||||
/obj/item/storage/box/emergencytank
|
||||
name = "emergency oxygen tank box"
|
||||
desc = "A box of emergency oxygen tanks."
|
||||
illustration = "emergencytank"
|
||||
|
||||
/obj/item/storage/box/emergencytank/PopulateContents()
|
||||
..()
|
||||
for(var/i in 1 to 7)
|
||||
new /obj/item/tank/internals/emergency_oxygen(src) //in case anyone ever wants to do anything with spawning them, apart from crafting the box
|
||||
|
||||
/obj/item/storage/box/engitank
|
||||
name = "extended-capacity emergency oxygen tank box"
|
||||
desc = "A box of extended-capacity emergency oxygen tanks."
|
||||
illustration = "extendedtank"
|
||||
|
||||
/obj/item/storage/box/engitank/PopulateContents()
|
||||
..()
|
||||
for(var/i in 1 to 7)
|
||||
new /obj/item/tank/internals/emergency_oxygen/engi(src) //in case anyone ever wants to do anything with spawning them, apart from crafting the box
|
||||
|
||||
/obj/item/storage/box/mre //base MRE type.
|
||||
name = "Nanotrasen MRE Ration Kit Menu 0"
|
||||
desc = "A package containing food suspended in an outdated bluespace pocket which lasts for centuries. If you're lucky you may even be able to enjoy the meal without getting food poisoning."
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
new /obj/item/clothing/neck/petcollar(src) //I considered removing the pet stuff too but eh, who knows. We might get Renault back. Plus I guess you could use that collar for... other means. Aren't you supposed to be guarding the disk?
|
||||
new /obj/item/pet_carrier(src)
|
||||
new /obj/item/clothing/suit/armor/vest/capcarapace(src)
|
||||
new /obj/item/clothing/suit/armor/vest/capcarapace/alt(src)
|
||||
new /obj/item/clothing/suit/toggle/captains_parade(src)
|
||||
new /obj/item/clothing/head/crown/fancy(src)
|
||||
new /obj/item/cartridge/captain(src)
|
||||
new /obj/item/storage/box/silver_ids(src)
|
||||
@@ -56,6 +56,7 @@
|
||||
/obj/structure/closet/secure_closet/hos/PopulateContents()
|
||||
..()
|
||||
new /obj/item/clothing/neck/cloak/hos(src)
|
||||
new /obj/item/clothing/suit/toggle/armor/hos/hos_formal(src)
|
||||
new /obj/item/cartridge/hos(src)
|
||||
new /obj/item/radio/headset/heads/hos(src)
|
||||
new /obj/item/clothing/under/rank/security/head_of_security/parade/female(src)
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ GLOBAL_LIST(topic_status_cache)
|
||||
call(debug_server, "auxtools_init")()
|
||||
enable_debugging()
|
||||
AUXTOOLS_CHECK(AUXMOS)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef EXTOOLS_REFERENCE_TRACKING
|
||||
enable_reference_tracking()
|
||||
#endif
|
||||
world.Profile(PROFILE_START)
|
||||
|
||||
@@ -195,7 +195,7 @@ GLOBAL_PROTECT(admin_verbs_debug)
|
||||
// #endif
|
||||
/datum/admins/proc/create_or_modify_area,
|
||||
/datum/admins/proc/fixcorruption,
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef EXTOOLS_REFERENCE_TRACKING
|
||||
/datum/admins/proc/view_refs,
|
||||
/datum/admins/proc/view_del_failures,
|
||||
#endif
|
||||
|
||||
@@ -2862,7 +2862,7 @@
|
||||
return
|
||||
|
||||
if(!CONFIG_GET(string/centcom_ban_db))
|
||||
to_chat(usr, "<span class='warning'>Centcom Galactic Ban DB is disabled!</span>")
|
||||
to_chat(usr, span_warning("Centcom Galactic Ban DB is disabled!"))
|
||||
return
|
||||
|
||||
var/ckey = href_list["centcomlookup"]
|
||||
@@ -2889,8 +2889,19 @@
|
||||
dat += "<center><b>0 bans detected for [ckey]</b></center>"
|
||||
else
|
||||
bans = json_decode(response["body"])
|
||||
dat += "<center><b>[bans.len] ban\s detected for [ckey]</b></center>"
|
||||
|
||||
//Ignore bans from non-whitelisted sources, if a whitelist exists
|
||||
var/list/valid_sources
|
||||
if(CONFIG_GET(string/centcom_source_whitelist))
|
||||
valid_sources = splittext(CONFIG_GET(string/centcom_source_whitelist), ",")
|
||||
dat += "<center><b>Bans detected for [ckey]</b></center>"
|
||||
else
|
||||
//Ban count is potentially inaccurate if they're using a whitelist
|
||||
dat += "<center><b>[bans.len] ban\s detected for [ckey]</b></center>"
|
||||
|
||||
for(var/list/ban in bans)
|
||||
if(valid_sources && !(ban["sourceName"] in valid_sources))
|
||||
continue
|
||||
dat += "<b>Server: </b> [sanitize(ban["sourceName"])]<br>"
|
||||
dat += "<b>RP Level: </b> [sanitize(ban["sourceRoleplayLevel"])]<br>"
|
||||
dat += "<b>Type: </b> [sanitize(ban["type"])]<br>"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef EXTOOLS_REFERENCE_TRACKING
|
||||
|
||||
GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
|
||||
@@ -102,29 +102,21 @@ GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LEGACY_REFERENCE_TRACKING
|
||||
#ifdef REFERENCE_TRACKING
|
||||
|
||||
/datum/verb/legacy_find_refs()
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set src in world
|
||||
|
||||
find_references_legacy(FALSE)
|
||||
|
||||
|
||||
/datum/proc/find_references_legacy(skip_alert)
|
||||
/datum/proc/find_references(skip_alert)
|
||||
running_find_references = type
|
||||
if(usr?.client)
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
log_reftracker("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = TRUE
|
||||
SSgarbage.next_fire = world.time + world.tick_lag
|
||||
SSgarbage.update_nextfire(reset_time = TRUE)
|
||||
return
|
||||
|
||||
if(!skip_alert && alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") != "Yes")
|
||||
if(!skip_alert && tgui_alert(usr,"Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", list("Yes", "No")) != "Yes")
|
||||
running_find_references = null
|
||||
return
|
||||
|
||||
@@ -134,92 +126,122 @@ GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = type
|
||||
|
||||
testing("Beginning search for references to a [type].")
|
||||
last_find_references = world.time
|
||||
log_reftracker("Beginning search for references to a [type].")
|
||||
|
||||
var/starting_time = world.time
|
||||
|
||||
//Time to search the whole game for our ref
|
||||
DoSearchVar(GLOB, "GLOB") //globals
|
||||
log_reftracker("Finished searching globals")
|
||||
|
||||
DoSearchVar(GLOB) //globals
|
||||
for(var/datum/thing in world) //atoms (don't beleive its lies)
|
||||
DoSearchVar(thing, "World -> [thing]")
|
||||
DoSearchVar(thing, "World -> [thing.type]", search_time = starting_time)
|
||||
log_reftracker("Finished searching atoms")
|
||||
|
||||
for(var/datum/thing) //datums
|
||||
DoSearchVar(thing, "World -> [thing]")
|
||||
DoSearchVar(thing, "Datums -> [thing.type]", search_time = starting_time)
|
||||
log_reftracker("Finished searching datums")
|
||||
|
||||
//Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself
|
||||
for(var/client/thing) //clients
|
||||
DoSearchVar(thing, "World -> [thing]")
|
||||
DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time)
|
||||
log_reftracker("Finished searching clients")
|
||||
|
||||
log_reftracker("Completed search for references to a [type].")
|
||||
|
||||
testing("Completed search for references to a [type].")
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = TRUE
|
||||
SSgarbage.next_fire = world.time + world.tick_lag
|
||||
SSgarbage.update_nextfire(reset_time = TRUE)
|
||||
|
||||
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64, search_time = world.time)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(!found_refs && SSgarbage.should_save_refs)
|
||||
found_refs = list()
|
||||
#endif
|
||||
|
||||
/datum/verb/qdel_then_find_references()
|
||||
set category = "Debug"
|
||||
set name = "qdel() then Find References"
|
||||
set src in world
|
||||
|
||||
qdel(src, TRUE) //force a qdel
|
||||
if(!running_find_references)
|
||||
find_references_legacy(TRUE)
|
||||
|
||||
|
||||
/datum/verb/qdel_then_if_fail_find_references()
|
||||
set category = "Debug"
|
||||
set name = "qdel() then Find References if GC failure"
|
||||
set src in world
|
||||
|
||||
qdel_and_find_ref_if_fail(src, TRUE)
|
||||
|
||||
|
||||
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64)
|
||||
if(usr?.client && !usr.client.running_find_references)
|
||||
return
|
||||
|
||||
if(!recursive_limit)
|
||||
log_reftracker("Recursion limit reached. [container_name]")
|
||||
return
|
||||
|
||||
if(istype(potential_container, /datum))
|
||||
var/datum/datum_container = potential_container
|
||||
if(datum_container.last_find_references == last_find_references)
|
||||
return
|
||||
|
||||
datum_container.last_find_references = last_find_references
|
||||
var/list/vars_list = datum_container.vars
|
||||
|
||||
for(var/varname in vars_list)
|
||||
if (varname == "vars")
|
||||
continue
|
||||
var/variable = vars_list[varname]
|
||||
|
||||
if(variable == src)
|
||||
testing("Found [type] \ref[src] in [datum_container.type]'s [varname] var. [container_name]")
|
||||
|
||||
else if(islist(variable))
|
||||
DoSearchVar(variable, "[container_name] -> list", recursive_limit - 1)
|
||||
|
||||
else if(islist(potential_container))
|
||||
var/normal = IS_NORMAL_LIST(potential_container)
|
||||
for(var/element_in_list in potential_container)
|
||||
if(element_in_list == src)
|
||||
testing("Found [type] \ref[src] in list [container_name].")
|
||||
|
||||
else if(element_in_list && !isnum(element_in_list) && normal && potential_container[element_in_list] == src)
|
||||
testing("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
|
||||
|
||||
else if(islist(element_in_list))
|
||||
DoSearchVar(element_in_list, "[container_name] -> list", recursive_limit - 1)
|
||||
|
||||
//Check each time you go down a layer. This makes it a bit slow, but it won't effect the rest of the game at all
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
|
||||
if(istype(potential_container, /datum))
|
||||
var/datum/datum_container = potential_container
|
||||
if(datum_container.last_find_references == search_time)
|
||||
return
|
||||
|
||||
datum_container.last_find_references = search_time
|
||||
var/list/vars_list = datum_container.vars
|
||||
|
||||
for(var/varname in vars_list)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
if (varname == "vars" || varname == "vis_locs") //Fun fact, vis_locs don't count for references
|
||||
continue
|
||||
var/variable = vars_list[varname]
|
||||
|
||||
if(variable == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[varname] = TRUE
|
||||
#endif
|
||||
log_reftracker("Found [type] \ref[src] in [datum_container.type]'s \ref[datum_container] [varname] var. [container_name]")
|
||||
continue
|
||||
|
||||
if(islist(variable))
|
||||
DoSearchVar(variable, "[container_name] \ref[datum_container] -> [varname] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
else if(islist(potential_container))
|
||||
var/normal = IS_NORMAL_LIST(potential_container)
|
||||
var/list/potential_cache = potential_container
|
||||
for(var/element_in_list in potential_cache)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
//Check normal entrys
|
||||
if(element_in_list == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
#endif
|
||||
log_reftracker("Found [type] \ref[src] in list [container_name].")
|
||||
continue
|
||||
|
||||
var/assoc_val = null
|
||||
if(!isnum(element_in_list) && normal)
|
||||
assoc_val = potential_cache[element_in_list]
|
||||
//Check assoc entrys
|
||||
if(assoc_val == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
#endif
|
||||
log_reftracker("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
|
||||
continue
|
||||
//We need to run both of these checks, since our object could be hiding in either of them
|
||||
//Check normal sublists
|
||||
if(islist(element_in_list))
|
||||
DoSearchVar(element_in_list, "[container_name] -> [element_in_list] (list)", recursive_limit - 1, search_time)
|
||||
//Check assoc sublists
|
||||
if(islist(assoc_val))
|
||||
DoSearchVar(potential_container[element_in_list], "[container_name]\[[element_in_list]\] -> [assoc_val] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
/proc/qdel_and_find_ref_if_fail(datum/thing_to_del, force = FALSE)
|
||||
SSgarbage.reference_find_on_fail[REF(thing_to_del)] = TRUE
|
||||
qdel(thing_to_del, force)
|
||||
thing_to_del.qdel_and_find_ref_if_fail(force)
|
||||
|
||||
/datum/proc/qdel_and_find_ref_if_fail(force = FALSE)
|
||||
SSgarbage.reference_find_on_fail["\ref[src]"] = TRUE
|
||||
qdel(src, force)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
usr.client.debug_variables(src)
|
||||
return
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef EXTOOLS_REFERENCE_TRACKING
|
||||
if(href_list[VV_HK_VIEW_REFERENCES])
|
||||
var/datum/D = locate(href_list[VV_HK_TARGET])
|
||||
if(!D)
|
||||
|
||||
+4
-4
@@ -253,7 +253,7 @@
|
||||
purpose_fulfilled = TRUE
|
||||
make_glow()
|
||||
animate(glow, transform = matrix() * 1.5, alpha = 255, time = 125)
|
||||
sound_to_playing_players(volume = 100, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/ratvar_rises.ogg')) //End the sounds
|
||||
sound_to_playing_players('sound/effects/ratvar_rises.ogg', 90, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
sleep(125)
|
||||
make_glow()
|
||||
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
|
||||
@@ -318,19 +318,19 @@
|
||||
if(-INFINITY to GATEWAY_REEBE_FOUND)
|
||||
if(!second_sound_played)
|
||||
sound_to_playing_players('sound/magic/clockwork/invoke_general.ogg', 30, FALSE)
|
||||
sound_to_playing_players(volume = 10, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_charging.ogg', TRUE))
|
||||
sound_to_playing_players('sound/effects/clockcult_gateway_charging.ogg', 10, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
second_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_charging"
|
||||
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
|
||||
if(!third_sound_played)
|
||||
sound_to_playing_players(volume = 30, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_active.ogg', TRUE))
|
||||
sound_to_playing_players('sound/effects/clockcult_gateway_active.ogg', 30, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
third_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_active"
|
||||
if(GATEWAY_RATVAR_COMING to GATEWAY_RATVAR_ARRIVAL)
|
||||
if(!fourth_sound_played)
|
||||
sound_to_playing_players(volume = 70, channel = CHANNEL_JUSTICAR_ARK, S = sound('sound/effects/clockcult_gateway_closing.ogg', TRUE))
|
||||
sound_to_playing_players('sound/effects/clockcult_gateway_closing.ogg', 70, FALSE, channel = CHANNEL_JUSTICAR_ARK)
|
||||
fourth_sound_played = TRUE
|
||||
make_glow()
|
||||
glow.icon_state = "clockwork_gateway_closing"
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
if((IS_HERETIC(local_user) || IS_HERETIC_MONSTER(local_user)) && HAS_TRAIT(src,TRAIT_NODROP))
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT)
|
||||
|
||||
for(var/mob/living/carbon/human/human_in_range in spiral_range(9,local_user))
|
||||
for(var/mob/living/carbon/human/human_in_range in viewers(9,local_user))
|
||||
if(IS_HERETIC(human_in_range) || IS_HERETIC_MONSTER(human_in_range))
|
||||
continue
|
||||
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
/datum/asset/simple/tgui_common
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui-common.bundle.js" = 'tgui/public/tgui-common.bundle.js',
|
||||
"tgui-common.bundle.js" = file("tgui/public/tgui-common.bundle.js"),
|
||||
)
|
||||
|
||||
/datum/asset/simple/tgui
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui.bundle.js" = 'tgui/public/tgui.bundle.js',
|
||||
"tgui.bundle.css" = 'tgui/public/tgui.bundle.css',
|
||||
"tgui.bundle.js" = file("tgui/public/tgui.bundle.js"),
|
||||
"tgui.bundle.css" = file("tgui/public/tgui.bundle.css"),
|
||||
)
|
||||
|
||||
/datum/asset/simple/tgui_panel
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui-panel.bundle.js" = 'tgui/public/tgui-panel.bundle.js',
|
||||
"tgui-panel.bundle.css" = 'tgui/public/tgui-panel.bundle.css',
|
||||
"tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"),
|
||||
"tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"),
|
||||
)
|
||||
|
||||
/datum/asset/simple/headers
|
||||
@@ -168,10 +168,12 @@
|
||||
|
||||
/datum/asset/simple/namespaced/tgfont
|
||||
assets = list(
|
||||
"tgfont.eot" = 'tgui/packages/tgfont/dist/tgfont.eot',
|
||||
"tgfont.woff2" = 'tgui/packages/tgfont/dist/tgfont.woff2',
|
||||
"tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"),
|
||||
"tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"),
|
||||
)
|
||||
parents = list(
|
||||
"tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"),
|
||||
)
|
||||
parents = list("tgfont.css" = 'tgui/packages/tgfont/dist/tgfont.css')
|
||||
|
||||
/datum/asset/spritesheet/chat
|
||||
name = "chat"
|
||||
|
||||
@@ -75,6 +75,9 @@
|
||||
/obj/machinery/atmospherics/pipe/setPipenet(datum/pipeline/P)
|
||||
parent = P
|
||||
|
||||
/obj/machinery/atmospherics/pipe/zap_act(power, zap_flags)
|
||||
return 0 // they're not really machines in the normal sense, probably shouldn't explode
|
||||
|
||||
/obj/machinery/atmospherics/pipe/Destroy()
|
||||
QDEL_NULL(parent)
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@
|
||||
cost = 1500
|
||||
contains = list(/obj/item/toy/plush/beeplushie)
|
||||
|
||||
/datum/supply_pack/goody/dyespray
|
||||
name = "Hair Dye Spray"
|
||||
desc = "A cool spray to dye your hair with awesome colors!"
|
||||
cost = PAYCHECK_EASY * 2
|
||||
contains = list(/obj/item/dyespray)
|
||||
|
||||
/datum/supply_pack/goody/beach_ball
|
||||
name = "Beach Ball"
|
||||
desc = "The simple beach ball is one of Nanotrasen's most popular products. 'Why do we make beach balls? Because we can! (TM)' - Nanotrasen"
|
||||
|
||||
@@ -119,6 +119,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/hair_color = "000000" //Hair color
|
||||
var/facial_hair_style = "Shaved" //Face hair type
|
||||
var/facial_hair_color = "000000" //Facial hair color
|
||||
var/grad_style //Hair gradient style
|
||||
var/grad_color = "FFFFFF" //Hair gradient color
|
||||
var/skin_tone = "caucasian1" //Skin color
|
||||
var/use_custom_skin_tone = FALSE
|
||||
var/left_eye_color = "000000" //Eye color
|
||||
@@ -505,6 +507,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<a href='?_src_=prefs;preference=previous_facehair_style;task=input'><</a> <a href='?_src_=prefs;preference=next_facehair_style;task=input'>></a><BR>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[facial_hair_color];'> </span> <a href='?_src_=prefs;preference=facial;task=input'>Change</a><BR>"
|
||||
|
||||
dat += "<h3>Hair Gradient</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=grad_style;task=input'>[grad_style]</a>"
|
||||
dat += "<a href='?_src_=prefs;preference=previous_grad_style;task=input'><</a> <a href='?_src_=prefs;preference=next_grad_style;task=input'>></a><BR>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[grad_color];'> </span> <a href='?_src_=prefs;preference=grad_color;task=input'>Change</a><BR>"
|
||||
|
||||
dat += "</td>"
|
||||
//Mutant stuff
|
||||
var/mutant_category = 0
|
||||
@@ -1711,6 +1719,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if("previous_facehair_style")
|
||||
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_list)
|
||||
|
||||
if("grad_color")
|
||||
var/new_grad_color = input(user, "Choose your character's gradient colour:", "Character Preference","#"+grad_color) as color|null
|
||||
if(new_grad_color)
|
||||
grad_color = sanitize_hexcolor(new_grad_color, 6)
|
||||
|
||||
if("grad_style")
|
||||
var/new_grad_style
|
||||
new_grad_style = input(user, "Choose your character's hair gradient style:", "Character Preference") as null|anything in GLOB.hair_gradients_list
|
||||
if(new_grad_style)
|
||||
grad_style = new_grad_style
|
||||
|
||||
if("next_grad_style")
|
||||
grad_style = next_list_item(grad_style, GLOB.hair_gradients_list)
|
||||
|
||||
if("previous_grad_style")
|
||||
grad_style = previous_list_item(grad_style, GLOB.hair_gradients_list)
|
||||
|
||||
if("cycle_bg")
|
||||
bgstate = next_list_item(bgstate, bgstate_options)
|
||||
|
||||
@@ -3016,6 +3041,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
character.dna.skin_tone_override = use_custom_skin_tone ? skin_tone : null
|
||||
character.hair_style = hair_style
|
||||
character.facial_hair_style = facial_hair_style
|
||||
character.grad_style = grad_style
|
||||
character.grad_color = grad_color
|
||||
character.underwear = underwear
|
||||
|
||||
character.saved_underwear = underwear
|
||||
|
||||
@@ -683,6 +683,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["skin_tone"] >> skin_tone
|
||||
S["hair_style_name"] >> hair_style
|
||||
S["facial_style_name"] >> facial_hair_style
|
||||
S["grad_style"] >> grad_style
|
||||
S["grad_color"] >> grad_color
|
||||
S["underwear"] >> underwear
|
||||
S["undie_color"] >> undie_color
|
||||
S["undershirt"] >> undershirt
|
||||
@@ -875,6 +877,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
|
||||
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
|
||||
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
|
||||
grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list, "None")
|
||||
grad_color = sanitize_hexcolor(grad_color, 6, FALSE)
|
||||
eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE)
|
||||
left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
|
||||
right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
|
||||
@@ -1044,6 +1048,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["skin_tone"] , skin_tone)
|
||||
WRITE_FILE(S["hair_style_name"] , hair_style)
|
||||
WRITE_FILE(S["facial_style_name"] , facial_hair_style)
|
||||
WRITE_FILE(S["grad_style"] , grad_style)
|
||||
WRITE_FILE(S["grad_color"] , grad_color)
|
||||
WRITE_FILE(S["underwear"] , underwear)
|
||||
WRITE_FILE(S["undie_color"] , undie_color)
|
||||
WRITE_FILE(S["undershirt"] , undershirt)
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
icon_state = "clown"
|
||||
item_state = "clown_hat"
|
||||
dye_color = "clown"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_cover = MASKCOVERSEYES
|
||||
resistance_flags = FLAMMABLE
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
@@ -131,6 +132,7 @@
|
||||
clothing_flags = ALLOWINTERNALS
|
||||
icon_state = "mime"
|
||||
item_state = "mime"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_cover = MASKCOVERSEYES
|
||||
resistance_flags = FLAMMABLE
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
|
||||
back = /obj/item/storage/backpack/captain
|
||||
belt = /obj/item/storage/belt/security/full
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun=1)
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert
|
||||
glasses = /obj/item/clothing/glasses/thermal/eyepatch
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/e_gun=1)
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
/datum/outfit/ert/commander/alert/red
|
||||
name = "ERT Commander - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/pulse/pistol/loyalpin=1)
|
||||
@@ -71,7 +71,7 @@
|
||||
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
|
||||
belt = /obj/item/storage/belt/security/full
|
||||
back = /obj/item/storage/backpack/security
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/storage/box/handcuffs=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun/stun=1,\
|
||||
@@ -91,7 +91,7 @@
|
||||
name = "ERT Security - Amber Alert"
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/sec
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/storage/box/handcuffs=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
/datum/outfit/ert/security/alert/red
|
||||
name = "ERT Security - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/storage/box/handcuffs=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
@@ -114,7 +114,7 @@
|
||||
back = /obj/item/storage/backpack/satchel/med
|
||||
belt = /obj/item/storage/belt/medical
|
||||
r_hand = /obj/item/storage/firstaid/regular
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -135,7 +135,7 @@
|
||||
name = "ERT Medic - Amber Alert"
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/med
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -144,7 +144,7 @@
|
||||
|
||||
/datum/outfit/ert/medic/alert/red
|
||||
name = "ERT Medic - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/pulse/pistol/loyalpin=1,\
|
||||
@@ -161,7 +161,7 @@
|
||||
belt = /obj/item/storage/belt/utility/full
|
||||
l_pocket = /obj/item/rcd_ammo/large
|
||||
r_hand = /obj/item/storage/firstaid/regular
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -181,7 +181,7 @@
|
||||
name = "ERT Engineer - Amber Alert"
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/alert/engi
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/e_gun=1,\
|
||||
@@ -189,7 +189,7 @@
|
||||
|
||||
/datum/outfit/ert/engineer/alert/red
|
||||
name = "ERT Engineer - Red Alert"
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
|
||||
/obj/item/melee/baton/loaded=1,\
|
||||
/obj/item/clothing/mask/gas/sechailer/swat=1,\
|
||||
/obj/item/gun/energy/pulse/pistol/loyalpin=1,\
|
||||
@@ -260,7 +260,7 @@
|
||||
name = "Inquisition Commander"
|
||||
r_hand = /obj/item/nullrod/scythe/talking/chainsword
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun=1)
|
||||
|
||||
@@ -269,7 +269,7 @@
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
|
||||
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/storage/box/handcuffs=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun/stun=1,
|
||||
@@ -281,7 +281,7 @@
|
||||
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
|
||||
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/melee/baton/loaded=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun=1,
|
||||
@@ -307,7 +307,7 @@
|
||||
glasses = /obj/item/clothing/glasses/hud/health
|
||||
back = /obj/item/storage/backpack/cultpack
|
||||
belt = /obj/item/storage/belt/soulstone
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/nullrod=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
/obj/item/gun/energy/e_gun=1,
|
||||
@@ -319,7 +319,7 @@
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
|
||||
|
||||
belt = /obj/item/storage/belt/soulstone/full/chappy
|
||||
backpack_contents = list(/obj/item/storage/box/engineer=1,
|
||||
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,
|
||||
/obj/item/grenade/chem_grenade/holy=1,
|
||||
/obj/item/nullrod=1,
|
||||
/obj/item/clothing/mask/gas/sechailer=1,
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
id = /obj/item/card/id/syndicate/locked_banking
|
||||
belt = /obj/item/gun/ballistic/automatic/pistol
|
||||
l_pocket = /obj/item/paper/fluff/vr/fluke_ops
|
||||
backpack_contents = list(/obj/item/storage/box/syndie=1,\
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
/obj/item/kitchen/knife/combat/survival)
|
||||
starting_funds = 0 //Should be operating, not shopping.
|
||||
|
||||
|
||||
@@ -237,12 +237,28 @@
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/mining/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/mining/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
hardsuit_type = "mining_goliath"
|
||||
if(amount == maxamount)
|
||||
hardsuit_type = "mining_goliath_full"
|
||||
icon_state = "hardsuit[on]-[hardsuit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.head == src)
|
||||
wearer.update_inv_head()
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/mining
|
||||
icon_state = "hardsuit-mining"
|
||||
name = "mining hardsuit"
|
||||
desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating for wildlife encounters."
|
||||
item_state = "mining_hardsuit"
|
||||
hardsuit_type = "mining"
|
||||
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
|
||||
resistance_flags = FIRE_PROOF
|
||||
armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75, "wound" = 15)
|
||||
@@ -254,6 +270,21 @@
|
||||
/obj/item/clothing/suit/space/hardsuit/mining/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/mining/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
hardsuit_type = "mining_goliath"
|
||||
if(amount == maxamount)
|
||||
hardsuit_type = "mining_goliath_full"
|
||||
icon_state = "hardsuit-[hardsuit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.wear_suit == src)
|
||||
wearer.update_inv_wear_suit()
|
||||
|
||||
//Syndicate hardsuit
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/syndi
|
||||
|
||||
@@ -136,17 +136,24 @@
|
||||
icon_state = "syndievest"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE
|
||||
|
||||
/obj/item/clothing/suit/armor/vest/capcarapace/alt
|
||||
/obj/item/clothing/suit/toggle/captains_parade
|
||||
name = "captain's parade jacket"
|
||||
desc = "For when an armoured vest isn't fashionable enough."
|
||||
icon_state = "capformal"
|
||||
item_state = "capspacesuit"
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 50, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90, "wound" = 10)
|
||||
togglename = "buttons"
|
||||
|
||||
/obj/item/clothing/suit/toggle/captains_parade/Initialize()
|
||||
. = ..()
|
||||
allowed = GLOB.security_wintercoat_allowed
|
||||
|
||||
/obj/item/clothing/suit/armor/riot
|
||||
name = "riot suit"
|
||||
desc = "A suit of semi-flexible polycarbonate body armor with heavy padding to protect against melee attacks. Helps the wearer resist shoving in close quarters."
|
||||
icon_state = "swat"
|
||||
item_state = "swat_suit"
|
||||
icon_state = "riot"
|
||||
item_state = "riot"
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
@@ -319,3 +326,29 @@
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
|
||||
armor = list("melee" = 25, "bullet" = 20, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 50, "rad" = 20, "fire" = -10, "acid" = 50, "wound" = 10)
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/vest/centcom_formal
|
||||
name = "\improper CentCom formal coat"
|
||||
desc = "A stylish coat given to CentCom Commanders. Perfect for sending ERTs to suicide missions with style!"
|
||||
icon_state = "centcom_formal"
|
||||
item_state = "centcom"
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
armor = list("melee" = 35, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 35, "bio" = 10, "rad" = 10, "fire" = 10, "acid" = 60)
|
||||
togglename = "buttons"
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/vest/centcom_formal/Initialize()
|
||||
. = ..()
|
||||
allowed = GLOB.security_wintercoat_allowed
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/hos/hos_formal
|
||||
name = "\improper Head of Security's parade jacket"
|
||||
desc = "For when an armoured vest isn't fashionable enough."
|
||||
icon_state = "hosformal"
|
||||
item_state = "hostrench"
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90, "wound" = 10)
|
||||
togglename = "buttons"
|
||||
|
||||
/obj/item/clothing/suit/toggle/armor/hos/hos_formal/Initialize()
|
||||
. = ..()
|
||||
allowed = GLOB.security_wintercoat_allowed
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
var/hoodtype = /obj/item/clothing/head/hooded/winterhood //so the chaplain hoodie or other hoodies can override this
|
||||
///Alternative mode for hiding the hood, instead of storing the hood in the suit it qdels it, useful for when you deal with hooded suit with storage.
|
||||
var/alternative_mode = FALSE
|
||||
var/no_t //do not update sprites when pulling up hood so we can avoid oddities with certain mechanics
|
||||
|
||||
/obj/item/clothing/suit/hooded/Initialize()
|
||||
. = ..()
|
||||
@@ -51,6 +52,8 @@
|
||||
update_icon()
|
||||
|
||||
/obj/item/clothing/suit/hooded/update_icon_state()
|
||||
if(no_t)
|
||||
return
|
||||
icon_state = "[initial(icon_state)]"
|
||||
if(ishuman(hood?.loc))
|
||||
var/mob/living/carbon/human/H = hood.loc
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#define CONE_WAFFLE 8
|
||||
#define CONE_CHOC 9
|
||||
|
||||
|
||||
|
||||
/obj/machinery/icecream_vat
|
||||
name = "ice cream vat"
|
||||
desc = "Ding-aling ding dong. Get your Nanotrasen-approved ice cream!"
|
||||
@@ -35,6 +33,8 @@
|
||||
/datum/reagent/consumable/ethanol/singulo = 6,
|
||||
/datum/reagent/consumable/peachjuice = 6,
|
||||
/datum/reagent/consumable/grapejuice = 6)
|
||||
var/custom_taste
|
||||
var/custom_color
|
||||
|
||||
/obj/machinery/icecream_vat/proc/get_ingredient_list(type)
|
||||
switch(type)
|
||||
@@ -99,7 +99,10 @@
|
||||
dat += "<b>Peach ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_PEACH]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_PEACH];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_PEACH];amount=5'><b>x5</b></a> [product_types[ICECREAM_PEACH]] dollops left. (Ingredients: milk, ice, peach juice)<br>"
|
||||
dat += "<b>Grape ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_GRAPE]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_GRAPE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_GRAPE];amount=5'><b>x5</b></a> [product_types[ICECREAM_GRAPE]] dollops left. (Ingredients: milk, ice, grape juice)<br>"
|
||||
dat += "<b>Blue ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_BLUE]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_BLUE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_BLUE];amount=5'><b>x5</b></a> [product_types[ICECREAM_BLUE]] dollops left. (Ingredients: milk, ice, singulo)<br>"
|
||||
dat += "<b>Custom ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_CUSTOM]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=5'><b>x5</b></a> [product_types[ICECREAM_CUSTOM]] dollops left. (Ingredients: milk, ice, optional flavoring)<br></div>"
|
||||
dat += "<b>Custom ice cream:</b> <a href='?src=[REF(src)];select=[ICECREAM_CUSTOM]'><b>Select</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[ICECREAM_CUSTOM];amount=5'><b>x5</b></a> [product_types[ICECREAM_CUSTOM]] dollops left. (Ingredients: milk, ice, optional flavoring)<br>"
|
||||
dat += "<a href='?src=[REF(src)];custom_taste=1;'><b>Change custom taste: [custom_taste ? custom_taste : "Default"]</b></a>"
|
||||
dat += "<br><a href='?src=[REF(src)];custom_color=1;'><b>Change custom color: [custom_color ? custom_color : "Default"]</b></a>"
|
||||
dat += "<br><a href='?src=[REF(src)];reset_custom=1;'><b>Reset custom ice cream taste and color to defaults</b></a></div>"
|
||||
dat += "<br><b>CONES</b><br><div class='statusDisplay'>"
|
||||
dat += "<b>Waffle cones:</b> <a href='?src=[REF(src)];cone=[CONE_WAFFLE]'><b>Dispense</b></a> <a href='?src=[REF(src)];make=[CONE_WAFFLE];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[CONE_WAFFLE];amount=5'><b>x5</b></a> [product_types[CONE_WAFFLE]] cones left. (Ingredients: flour, sugar)<br>"
|
||||
dat += "<b>Chocolate cones:</b> <a href='?src=[REF(src)];cone=[CONE_CHOC]'><b>Dispense</b></a> <a href='?src=[REF(src)];make=[CONE_CHOC];amount=1'><b>Make</b></a> <a href='?src=[REF(src)];make=[CONE_CHOC];amount=5'><b>x5</b></a> [product_types[CONE_CHOC]] cones left. (Ingredients: flour, sugar, coco powder)<br></div>"
|
||||
@@ -128,7 +131,7 @@
|
||||
visible_message("[icon2html(src, viewers(src))] <span class='info'>[user] scoops delicious [flavour_name] ice cream into [I].</span>")
|
||||
product_types[dispense_flavour] -= 1
|
||||
if(beaker && beaker.reagents.total_volume)
|
||||
I.add_ice_cream(flavour_name, beaker.reagents)
|
||||
I.add_ice_cream(flavour_name, beaker.reagents, custom_color, custom_taste)
|
||||
else
|
||||
I.add_ice_cream(flavour_name)
|
||||
if(I.reagents.total_volume < 10)
|
||||
@@ -213,14 +216,25 @@
|
||||
if(href_list["refill"])
|
||||
RefillFromBeaker()
|
||||
|
||||
updateDialog()
|
||||
|
||||
if(href_list["refresh"])
|
||||
updateDialog()
|
||||
|
||||
if(href_list["close"])
|
||||
usr.unset_machine()
|
||||
usr << browse(null,"window=icecreamvat")
|
||||
|
||||
if(href_list["custom_taste"])
|
||||
custom_taste = stripped_input(usr, "Set a custom taste for the custom icecream. 50 characters max, leave blank to go back to the default option.", max_length = 50)
|
||||
|
||||
if(href_list["custom_color"])
|
||||
custom_color = input(usr, "Choose a color for the custom icecream. Cancel to go back to the default option.") as color|null
|
||||
|
||||
if(href_list["reset_custom"])
|
||||
custom_taste = null
|
||||
custom_color = null
|
||||
|
||||
updateDialog() // i have no clue why we even have refresh when this is a thing but sure
|
||||
|
||||
return
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/icecream
|
||||
@@ -251,7 +265,7 @@
|
||||
desc = "Delicious [cone_name] cone, but no ice cream."
|
||||
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/icecream/proc/add_ice_cream(flavour_name, datum/reagents/R)
|
||||
/obj/item/reagent_containers/food/snacks/icecream/proc/add_ice_cream(flavour_name, datum/reagents/R, custom_color, custom_taste)
|
||||
name = "[flavour_name] icecream"
|
||||
switch (flavour_name) // adding the actual reagents advertised in the ingredient list
|
||||
if ("vanilla")
|
||||
@@ -286,8 +300,11 @@
|
||||
if(R && R.total_volume >= 4) //consumable reagents have stronger taste so higher volume will allow non-food flavourings to break through better.
|
||||
var/mutable_appearance/flavoring = mutable_appearance(icon,"icecream_custom")
|
||||
var/datum/reagent/master = R.get_master_reagent()
|
||||
flavoring.color = master.color
|
||||
filling_color = master.color
|
||||
flavoring.color = custom_color ? custom_color : master.color
|
||||
filling_color = custom_color ? custom_color : master.color
|
||||
if(custom_taste)
|
||||
tastes = list("[custom_taste]" = 1)
|
||||
reagents.force_alt_taste = TRUE
|
||||
name = "[master.name] icecream"
|
||||
desc = "A delicious [cone_type] cone filled with artisanal icecream. Made with real [master.name]. Ain't that something."
|
||||
R.trans_to(src, 4)
|
||||
|
||||
@@ -641,6 +641,18 @@ Since Ramadan is an entire month that lasts 29.5 days on average, the start and
|
||||
/datum/holiday/easter/getStationPrefix()
|
||||
return pick("Fluffy","Bunny","Easter","Egg")
|
||||
|
||||
/datum/holiday/ianbirthday
|
||||
name = "Ian's Birthday" //github.com/tgstation/tgstation/commit/de7e4f0de0d568cd6e1f0d7bcc3fd34700598acb
|
||||
begin_month = SEPTEMBER
|
||||
begin_day = 9
|
||||
end_day = 10
|
||||
|
||||
/datum/holiday/ianbirthday/greet()
|
||||
return "Happy birthday, Ian!"
|
||||
|
||||
/datum/holiday/ianbirthday/getStationPrefix()
|
||||
return pick("Ian", "Corgi", "Erro")
|
||||
|
||||
//Random citadel thing for halloween species
|
||||
/proc/force_enable_halloween_species()
|
||||
var/list/oldlist = SSevents.holidays
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
backpack = /obj/item/storage/backpack/industrial
|
||||
satchel = /obj/item/storage/backpack/satchel/eng
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
|
||||
box = /obj/item/storage/box/engineer
|
||||
box = /obj/item/storage/box/survival/engineer
|
||||
pda_slot = SLOT_L_STORE
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
backpack = /obj/item/storage/backpack/industrial
|
||||
satchel = /obj/item/storage/backpack/satchel/eng
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
|
||||
box = /obj/item/storage/box/engineer
|
||||
box = /obj/item/storage/box/survival/engineer
|
||||
pda_slot = SLOT_L_STORE
|
||||
chameleon_extras = /obj/item/stamp/ce
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
backpack = /obj/item/storage/backpack/security
|
||||
satchel = /obj/item/storage/backpack/satchel/sec
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/sec
|
||||
box = /obj/item/storage/box/security
|
||||
box = /obj/item/storage/box/survival/security
|
||||
|
||||
implants = list(/obj/item/implant/mindshield)
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S
|
||||
backpack = /obj/item/storage/backpack/security
|
||||
satchel = /obj/item/storage/backpack/satchel/sec
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/sec
|
||||
box = /obj/item/storage/box/security
|
||||
box = /obj/item/storage/box/survival/security
|
||||
|
||||
implants = list(/obj/item/implant/mindshield)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
backpack = /obj/item/storage/backpack/explorer
|
||||
satchel = /obj/item/storage/backpack/satchel/explorer
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag
|
||||
box = /obj/item/storage/box/survival_mining
|
||||
box = /obj/item/storage/box/survival/mining
|
||||
|
||||
chameleon_extras = /obj/item/gun/energy/kinetic_accelerator
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
backpack = /obj/item/storage/backpack/industrial
|
||||
satchel = /obj/item/storage/backpack/satchel/eng
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/engineering
|
||||
box = /obj/item/storage/box/engineer
|
||||
box = /obj/item/storage/box/survival/engineer
|
||||
pda_slot = SLOT_L_STORE
|
||||
backpack_contents = list(/obj/item/modular_computer/tablet/preset/advanced=1)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
backpack = /obj/item/storage/backpack/security
|
||||
satchel = /obj/item/storage/backpack/satchel/sec
|
||||
duffelbag = /obj/item/storage/backpack/duffelbag/sec
|
||||
box = /obj/item/storage/box/security
|
||||
box = /obj/item/storage/box/survival/security
|
||||
|
||||
implants = list(/obj/item/implant/mindshield)
|
||||
|
||||
|
||||
@@ -8,61 +8,73 @@
|
||||
..()
|
||||
return late ? INITIALIZE_HINT_LATELOAD : INITIALIZE_HINT_QDEL
|
||||
|
||||
|
||||
//airlock helpers
|
||||
/obj/effect/mapping_helpers/airlock
|
||||
layer = DOOR_HELPER_LAYER
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(!airlock)
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
else
|
||||
payload(airlock)
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/proc/payload(obj/machinery/door/airlock/payload)
|
||||
return
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper
|
||||
name = "airlock cyclelink helper"
|
||||
icon_state = "airlock_cyclelink_helper"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
if(airlock.cyclelinkeddir)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
|
||||
else
|
||||
airlock.cyclelinkeddir = dir
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.cyclelinkeddir)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
|
||||
else
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
airlock.cyclelinkeddir = dir
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi
|
||||
name = "airlock multi-cyclelink helper"
|
||||
icon_state = "airlock_multicyclelink_helper"
|
||||
var/cycle_id
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.closeOtherId)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] closeOtherId, but it's already set!")
|
||||
else
|
||||
airlock.closeOtherId = cycle_id
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/locked
|
||||
name = "airlock lock helper"
|
||||
icon_state = "airlock_locked_helper"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/locked/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
if(airlock.locked)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
|
||||
else
|
||||
airlock.locked = TRUE
|
||||
/obj/effect/mapping_helpers/airlock/locked/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.locked)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
|
||||
else
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
airlock.locked = TRUE
|
||||
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/unres
|
||||
name = "airlock unresctricted side helper"
|
||||
icon_state = "airlock_unres_helper"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/unres/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
airlock.unres_sides ^= dir
|
||||
/obj/effect/mapping_helpers/airlock/unres/payload(obj/machinery/door/airlock/airlock)
|
||||
airlock.unres_sides ^= dir
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/abandoned
|
||||
name = "airlock abandoned helper"
|
||||
icon_state = "airlock_abandoned"
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/abandoned/payload(obj/machinery/door/airlock/airlock)
|
||||
if(airlock.abandoned)
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to make [airlock] abandoned but it's already abandoned!")
|
||||
else
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
airlock.abandoned = TRUE
|
||||
|
||||
|
||||
//needs to do its thing before spawn_rivers() is called
|
||||
@@ -79,9 +91,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
//This helper applies components to things on the map directly.
|
||||
/obj/effect/mapping_helpers/component_injector
|
||||
name = "Component Injector"
|
||||
icon_state = "component"
|
||||
late = TRUE
|
||||
var/target_type
|
||||
var/target_name
|
||||
var/all = FALSE //Will inject into all fitting the criteria if true, otherwise first found
|
||||
var/target_type //Will inject into atoms of this type
|
||||
var/target_name //Will inject into atoms with this name
|
||||
var/component_type
|
||||
|
||||
//Late init so everything is likely ready and loaded (no warranty)
|
||||
@@ -98,8 +112,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
continue
|
||||
var/cargs = build_args()
|
||||
A._AddComponent(cargs)
|
||||
if(!all)
|
||||
qdel(src)
|
||||
return
|
||||
if(all)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/effect/mapping_helpers/component_injector/proc/build_args()
|
||||
return list(component_type)
|
||||
@@ -115,3 +132,276 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
CRASH("Wrong disease type passed in.")
|
||||
var/datum/disease/D = new disease_type()
|
||||
return list(component_type,D)
|
||||
|
||||
// /obj/effect/mapping_helpers/component_injector/areabound
|
||||
// name = "Areabound Injector"
|
||||
// icon_state = "component_areabound"
|
||||
// component_type = /datum/component/areabound
|
||||
// target_type = /atom/movable
|
||||
|
||||
/obj/effect/mapping_helpers/dead_body_placer
|
||||
name = "Dead Body placer"
|
||||
late = TRUE
|
||||
icon_state = "deadbodyplacer"
|
||||
var/bodycount = 2 //number of bodies to spawn
|
||||
|
||||
/obj/effect/mapping_helpers/dead_body_placer/LateInitialize()
|
||||
var/area/a = get_area(src)
|
||||
var/list/trays = list()
|
||||
for (var/i in a.contents)
|
||||
if (istype(i, /obj/structure/bodycontainer/morgue))
|
||||
trays += i
|
||||
if(!trays.len)
|
||||
log_mapping("[src] at [x],[y] could not find any morgues.")
|
||||
return
|
||||
for (var/i = 1 to bodycount)
|
||||
var/obj/structure/bodycontainer/morgue/j = pick(trays)
|
||||
var/mob/living/carbon/human/h = new /mob/living/carbon/human(j, 1)
|
||||
h.death()
|
||||
for (var/part in h.internal_organs) //randomly remove organs from each body, set those we keep to be in stasis
|
||||
if (prob(40))
|
||||
qdel(part)
|
||||
else
|
||||
var/obj/item/organ/O = part
|
||||
O.organ_flags |= ORGAN_FROZEN
|
||||
j.update_appearance()
|
||||
qdel(src)
|
||||
|
||||
|
||||
//On Ian's birthday, the hop's office is decorated.
|
||||
/obj/effect/mapping_helpers/ianbirthday
|
||||
name = "Ian's Bday Helper"
|
||||
late = TRUE
|
||||
icon_state = "iansbdayhelper"
|
||||
var/balloon_clusters = 2
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/LateInitialize()
|
||||
if(locate(/datum/holiday/ianbirthday) in SSevents.holidays)
|
||||
birthday()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/proc/birthday()
|
||||
var/area/a = get_area(src)
|
||||
var/list/table = list()//should only be one aka the front desk, but just in case...
|
||||
var/list/openturfs = list()
|
||||
|
||||
//confetti and a corgi balloon! (and some list stuff for more decorations)
|
||||
for(var/thing in a.contents)
|
||||
if(istype(thing, /obj/structure/table/reinforced))
|
||||
table += thing
|
||||
if(isopenturf(thing))
|
||||
// new /obj/effect/decal/cleanable/confetti(thing)
|
||||
// if(locate(/obj/structure/bed/dogbed/ian) in thing)
|
||||
// new /obj/item/toy/balloon/corgi(thing)
|
||||
// else
|
||||
openturfs += thing
|
||||
|
||||
//cake + knife to cut it!
|
||||
if(length(table))
|
||||
var/turf/food_turf = get_turf(pick(table))
|
||||
new /obj/item/kitchen/knife(food_turf)
|
||||
var/obj/item/reagent_containers/food/snacks/store/cake/birthday/iancake = new(food_turf)
|
||||
iancake.desc = "Happy birthday, Ian!"
|
||||
// remind me to give ian proper baloons!
|
||||
//some balloons! this picks an open turf and pops a few balloons in and around that turf, yay.
|
||||
// for(var/i in 1 to balloon_clusters)
|
||||
// var/turf/clusterspot = pick_n_take(openturfs)
|
||||
// new /obj/item/toy/balloon(clusterspot)
|
||||
// var/balloons_left_to_give = 3 //the amount of balloons around the cluster
|
||||
// var/list/dirs_to_balloon = GLOB.cardinals.Copy()
|
||||
// while(balloons_left_to_give > 0)
|
||||
// balloons_left_to_give--
|
||||
// var/chosen_dir = pick_n_take(dirs_to_balloon)
|
||||
// var/turf/balloonstep = get_step(clusterspot, chosen_dir)
|
||||
// var/placed = FALSE
|
||||
// if(isopenturf(balloonstep))
|
||||
// var/obj/item/toy/balloon/B = new(balloonstep)//this clumps the cluster together
|
||||
// placed = TRUE
|
||||
// if(chosen_dir == NORTH)
|
||||
// B.pixel_y -= 10
|
||||
// if(chosen_dir == SOUTH)
|
||||
// B.pixel_y += 10
|
||||
// if(chosen_dir == EAST)
|
||||
// B.pixel_x -= 10
|
||||
// if(chosen_dir == WEST)
|
||||
// B.pixel_x += 10
|
||||
// if(!placed)
|
||||
// new /obj/item/toy/balloon(clusterspot)
|
||||
//remind me to add wall decor!
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/admin//so admins may birthday any room
|
||||
name = "generic birthday setup"
|
||||
icon_state = "bdayhelper"
|
||||
|
||||
/obj/effect/mapping_helpers/ianbirthday/admin/LateInitialize()
|
||||
birthday()
|
||||
qdel(src)
|
||||
|
||||
//Ian, like most dogs, loves a good new years eve party.
|
||||
/obj/effect/mapping_helpers/iannewyear
|
||||
name = "Ian's New Years Helper"
|
||||
late = TRUE
|
||||
icon_state = "iansnewyrshelper"
|
||||
|
||||
/obj/effect/mapping_helpers/iannewyear/LateInitialize()
|
||||
if(SSevents.holidays && SSevents.holidays[NEW_YEAR])
|
||||
fireworks()
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/mapping_helpers/iannewyear/proc/fireworks()
|
||||
var/area/a = get_area(src)
|
||||
var/list/table = list()//should only be one aka the front desk, but just in case...
|
||||
var/list/openturfs = list()
|
||||
|
||||
for(var/thing in a.contents)
|
||||
if(istype(thing, /obj/structure/table/reinforced))
|
||||
table += thing
|
||||
else if(isopenturf(thing))
|
||||
if(locate(/obj/structure/bed/dogbed/ian) in thing)
|
||||
new /obj/item/clothing/head/festive(thing)
|
||||
var/obj/item/reagent_containers/food/drinks/bottle/champagne/iandrink = new(thing)
|
||||
iandrink.name = "dog champagne"
|
||||
iandrink.pixel_y += 8
|
||||
iandrink.pixel_x += 8
|
||||
else
|
||||
openturfs += thing
|
||||
|
||||
var/turf/fireworks_turf = get_turf(pick(table))
|
||||
var/obj/item/storage/box/matches/matchbox = new(fireworks_turf)
|
||||
matchbox.pixel_y += 8
|
||||
matchbox.pixel_x -= 3
|
||||
// new /obj/item/storage/box/fireworks/dangerous(fireworks_turf) //dangerous version for extra holiday memes.
|
||||
|
||||
//lets mappers place notes on airlocks with custom info or a pre-made note from a path
|
||||
/obj/effect/mapping_helpers/airlock_note_placer
|
||||
name = "Airlock Note Placer"
|
||||
late = TRUE
|
||||
icon_state = "airlocknoteplacer"
|
||||
var/note_info //for writing out custom notes without creating an extra paper subtype
|
||||
var/note_name //custom note name
|
||||
var/note_path //if you already have something wrote up in a paper subtype, put the path here
|
||||
|
||||
/obj/effect/mapping_helpers/airlock_note_placer/LateInitialize()
|
||||
var/turf/turf = get_turf(src)
|
||||
if(note_path && !istype(note_path, /obj/item/paper)) //don't put non-paper in the paper slot thank you
|
||||
log_mapping("[src] at [x],[y] had an improper note_path path, could not place paper note.")
|
||||
qdel(src)
|
||||
if(locate(/obj/machinery/door/airlock) in turf)
|
||||
var/obj/machinery/door/airlock/found_airlock = locate(/obj/machinery/door/airlock) in turf
|
||||
if(note_path)
|
||||
found_airlock.note = note_path
|
||||
found_airlock.update_appearance()
|
||||
qdel(src)
|
||||
if(note_info)
|
||||
var/obj/item/paper/paper = new /obj/item/paper(src)
|
||||
if(note_name)
|
||||
paper.name = note_name
|
||||
paper.info = "[note_info]"
|
||||
found_airlock.note = paper
|
||||
paper.forceMove(found_airlock)
|
||||
found_airlock.update_appearance()
|
||||
qdel(src)
|
||||
log_mapping("[src] at [x],[y] had no note_path or note_info, cannot place paper note.")
|
||||
qdel(src)
|
||||
log_mapping("[src] at [x],[y] could not find an airlock on current turf, cannot place paper note.")
|
||||
qdel(src)
|
||||
|
||||
//This helper applies traits to things on the map directly.
|
||||
/obj/effect/mapping_helpers/trait_injector
|
||||
name = "Trait Injector"
|
||||
icon_state = "trait"
|
||||
late = TRUE
|
||||
///Will inject into all fitting the criteria if false, otherwise first found.
|
||||
var/first_match_only = TRUE
|
||||
///Will inject into atoms of this type.
|
||||
var/target_type
|
||||
///Will inject into atoms with this name.
|
||||
var/target_name
|
||||
///Name of the trait, in the lower-case text (NOT the upper-case define) form.
|
||||
var/trait_name
|
||||
|
||||
//Late init so everything is likely ready and loaded (no warranty)
|
||||
/obj/effect/mapping_helpers/trait_injector/LateInitialize()
|
||||
if(!GLOB.trait_name_map)
|
||||
GLOB.trait_name_map = generate_trait_name_map()
|
||||
if(!GLOB.trait_name_map.Find(trait_name))
|
||||
CRASH("Wrong trait in [type] - [trait_name] is not a trait")
|
||||
var/turf/target_turf = get_turf(src)
|
||||
var/matches_found = 0
|
||||
for(var/a in target_turf.GetAllContents())
|
||||
var/atom/atom_on_turf = a
|
||||
if(atom_on_turf == src)
|
||||
continue
|
||||
if(target_name && atom_on_turf.name != target_name)
|
||||
continue
|
||||
if(target_type && !istype(atom_on_turf,target_type))
|
||||
continue
|
||||
ADD_TRAIT(atom_on_turf, trait_name, MAPPING_HELPER_TRAIT)
|
||||
matches_found++
|
||||
if(first_match_only)
|
||||
qdel(src)
|
||||
return
|
||||
if(!matches_found)
|
||||
stack_trace("Trait mapper found no targets at ([x], [y], [z]). First Match Only: [first_match_only ? "true" : "false"] target type: [target_type] | target name: [target_name] | trait name: [trait_name]")
|
||||
qdel(src)
|
||||
|
||||
/// Fetches an external dmi and applies to the target object
|
||||
/obj/effect/mapping_helpers/custom_icon
|
||||
name = "Custom Icon Helper"
|
||||
icon_state = "trait"
|
||||
late = TRUE
|
||||
///Will inject into all fitting the criteria if false, otherwise first found.
|
||||
var/first_match_only = TRUE
|
||||
///Will inject into atoms of this type.
|
||||
var/target_type
|
||||
///Will inject into atoms with this name.
|
||||
var/target_name
|
||||
/// This is the var tha will be set with the fetched icon. In case you want to set some secondary icon sheets like inhands and such.
|
||||
var/target_variable = "icon"
|
||||
/// This should return raw dmi in response to http get request. For example: "https://github.com/tgstation/SS13-sprites/raw/master/mob/medu.dmi?raw=true"
|
||||
var/icon_url
|
||||
|
||||
/obj/effect/mapping_helpers/custom_icon/LateInitialize()
|
||||
///TODO put this injector stuff under common root
|
||||
var/I = fetch_icon(icon_url)
|
||||
var/turf/target_turf = get_turf(src)
|
||||
var/matches_found = 0
|
||||
for(var/a in target_turf.GetAllContents())
|
||||
var/atom/atom_on_turf = a
|
||||
if(atom_on_turf == src)
|
||||
continue
|
||||
if(target_name && atom_on_turf.name != target_name)
|
||||
continue
|
||||
if(target_type && !istype(atom_on_turf,target_type))
|
||||
continue
|
||||
atom_on_turf.vars[target_variable] = I
|
||||
matches_found++
|
||||
if(first_match_only)
|
||||
qdel(src)
|
||||
return
|
||||
if(!matches_found)
|
||||
stack_trace("[src] found no targets at ([x], [y], [z]). First Match Only: [first_match_only ? "true" : "false"] target type: [target_type] | target name: [target_name]")
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/mapping_helpers/custom_icon/proc/fetch_icon(url)
|
||||
var/static/icon_cache = list()
|
||||
var/static/query_in_progress = FALSE //We're using a single tmp file so keep it linear.
|
||||
if(query_in_progress)
|
||||
UNTIL(!query_in_progress)
|
||||
if(icon_cache[url])
|
||||
return icon_cache[url]
|
||||
log_asset("Custom Icon Helper fetching dmi from: [url]")
|
||||
var/datum/http_request/request = new()
|
||||
var/file_name = "tmp/custom_map_icon.dmi"
|
||||
request.prepare(RUSTG_HTTP_METHOD_GET, url , "", "", file_name)
|
||||
query_in_progress = TRUE
|
||||
request.begin_async()
|
||||
UNTIL(request.is_complete())
|
||||
var/datum/http_response/response = request.into_response()
|
||||
if(response.errored || response.status_code != 200)
|
||||
query_in_progress = FALSE
|
||||
CRASH("Failed to fetch mapped custom icon from url [url], code: [response.status_code], error: [response.error]")
|
||||
var/icon/I = new(file_name)
|
||||
icon_cache[url] = I
|
||||
query_in_progress = FALSE
|
||||
return I
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/baseturf_helper/proc/replace_baseturf(turf/thing)
|
||||
var/list/baseturf_cache = thing.baseturfs
|
||||
if(length(baseturf_cache))
|
||||
if(length(thing.baseturfs))
|
||||
var/list/baseturf_cache = thing.baseturfs.Copy()
|
||||
for(var/i in baseturf_cache)
|
||||
if(baseturf_to_replace[i])
|
||||
baseturf_cache -= i
|
||||
@@ -44,6 +44,8 @@
|
||||
else
|
||||
thing.PlaceOnBottom(null, baseturf)
|
||||
|
||||
|
||||
|
||||
/obj/effect/baseturf_helper/space
|
||||
name = "space baseturf editor"
|
||||
baseturf = /turf/open/space
|
||||
@@ -79,4 +81,3 @@
|
||||
/obj/effect/baseturf_helper/lava_land/surface
|
||||
name = "lavaland baseturf editor"
|
||||
baseturf = /turf/open/lava/smooth/lava_land_surface
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/obj/item/clothing/suit/hooded/explorer
|
||||
name = "explorer suit"
|
||||
desc = "An armoured suit for exploring harsh environments."
|
||||
icon_state = "explorer"
|
||||
item_state = "explorer"
|
||||
icon_state = "explorer-normal"
|
||||
item_state = "explorer-normal"
|
||||
var/suit_type = "normal"
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
cold_protection = CHEST|GROIN|LEGS|ARMS
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
@@ -13,11 +14,15 @@
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe)
|
||||
resistance_flags = FIRE_PROOF
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_SNEK_TAURIC|STYLE_PAW_TAURIC
|
||||
no_t = TRUE
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer
|
||||
name = "explorer hood"
|
||||
desc = "An armoured hood for exploring harsh environments."
|
||||
icon_state = "explorer"
|
||||
icon_state = "explorer-normal"
|
||||
item_state = "explorer-normal"
|
||||
var/suit_type = "normal"
|
||||
var/basestate = "normal"
|
||||
body_parts_covered = HEAD
|
||||
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
@@ -33,10 +38,40 @@
|
||||
/obj/item/clothing/suit/hooded/explorer/standard/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/suit/hooded/explorer/standard/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
suit_type = "normal_goliath"
|
||||
if(amount == maxamount)
|
||||
suit_type = "normal_goliath_full"
|
||||
icon_state = "explorer-[suit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.wear_suit == src)
|
||||
wearer.update_inv_wear_suit()
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer/standard/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor_plate)
|
||||
RegisterSignal(src, COMSIG_ARMOR_PLATED, .proc/upgrade_icon)
|
||||
|
||||
/obj/item/clothing/head/hooded/explorer/standard/proc/upgrade_icon(datum/source, amount, maxamount)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(amount)
|
||||
name = "reinforced [initial(name)]"
|
||||
suit_type = "normal_goliath"
|
||||
if(amount == maxamount)
|
||||
suit_type = "normal_goliath_full"
|
||||
icon_state = "explorer-[suit_type]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
if(wearer.head == src)
|
||||
wearer.update_inv_head()
|
||||
|
||||
/obj/item/clothing/mask/gas/explorer
|
||||
name = "explorer gas mask"
|
||||
|
||||
@@ -1058,3 +1058,58 @@
|
||||
/datum/sprite_accessory/hair/zone
|
||||
name = "Zone"
|
||||
icon_state = "hair_zone"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient
|
||||
icon = 'icons/mob/hair_gradients.dmi'
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/none
|
||||
name = "None"
|
||||
icon_state = "none"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/fadeup
|
||||
name = "Fade Up"
|
||||
icon_state = "fadeup"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/fadedown
|
||||
name = "Fade Down"
|
||||
icon_state = "fadedown"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/vertical_split
|
||||
name = "Vertical Split"
|
||||
icon_state = "vsplit"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/_split
|
||||
name = "Horizontal Split"
|
||||
icon_state = "bottomflat"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/reflected
|
||||
name = "Reflected"
|
||||
icon_state = "reflected_high"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/reflected_inverse
|
||||
name = "Reflected Inverse"
|
||||
icon_state = "reflected_inverse_high"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/wavy
|
||||
name = "Wavy"
|
||||
icon_state = "wavy"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/long_fade_up
|
||||
name = "Long Fade Up"
|
||||
icon_state = "long_fade_up"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/long_fade_down
|
||||
name = "Long Fade Down"
|
||||
icon_state = "long_fade_down"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/short_fade_up
|
||||
name = "Short Fade Up"
|
||||
icon_state = "short_fade_up"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/short_fade_down
|
||||
name = "Short Fade Down"
|
||||
icon_state = "short_fade_down"
|
||||
|
||||
/datum/sprite_accessory/hair_gradient/wavy_spike
|
||||
name = "Spiked Wavy"
|
||||
icon_state = "wavy_spiked"
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
var/hair_color = "000"
|
||||
var/hair_style = "Bald"
|
||||
|
||||
///Colour used for the hair gradient.
|
||||
var/grad_color = "000"
|
||||
///Style used for the hair gradient.
|
||||
var/grad_style
|
||||
|
||||
//Facial hair colour and style
|
||||
var/facial_hair_color = "000"
|
||||
var/facial_hair_style = "Shaved"
|
||||
|
||||
@@ -52,6 +52,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/hair_color
|
||||
///The alpha used by the hair. 255 is completely solid, 0 is invisible.
|
||||
var/hair_alpha = 255
|
||||
///The gradient style used for the mob's hair.
|
||||
var/grad_style
|
||||
///The gradient color used to color the gradient.
|
||||
var/grad_color
|
||||
|
||||
///Does the species use skintones or not? As of now only used by humans.
|
||||
var/use_skintones = FALSE
|
||||
@@ -678,6 +682,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
if(!hair_hidden || dynamic_hair_suffix)
|
||||
var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
|
||||
var/mutable_appearance/gradient_overlay = mutable_appearance(layer = -HAIR_LAYER)
|
||||
if(!hair_hidden && !H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
|
||||
if(!(NOBLOOD in species_traits))
|
||||
hair_overlay.icon = 'icons/mob/human_parts.dmi'
|
||||
@@ -713,8 +718,21 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
hair_overlay.color = "#" + hair_color
|
||||
else
|
||||
hair_overlay.color = "#" + H.hair_color
|
||||
|
||||
//Gradients
|
||||
grad_style = H.grad_style
|
||||
grad_color = H.grad_color
|
||||
if(grad_style)
|
||||
var/datum/sprite_accessory/gradient = GLOB.hair_gradients_list[grad_style]
|
||||
var/icon/temp = icon(gradient.icon, gradient.icon_state)
|
||||
var/icon/temp_hair = icon(hair_file, hair_state)
|
||||
temp.Blend(temp_hair, ICON_ADD)
|
||||
gradient_overlay.icon = temp
|
||||
gradient_overlay.color = "#" + grad_color
|
||||
|
||||
else
|
||||
hair_overlay.color = forced_colour
|
||||
|
||||
hair_overlay.alpha = hair_alpha
|
||||
|
||||
if(OFFSET_HAIR in H.dna.species.offset_features)
|
||||
@@ -723,6 +741,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
if(hair_overlay.icon)
|
||||
standing += hair_overlay
|
||||
standing += gradient_overlay
|
||||
|
||||
if(standing.len)
|
||||
H.overlays_standing[HAIR_LAYER] = standing
|
||||
|
||||
@@ -103,13 +103,16 @@
|
||||
var/_method = override[thing]
|
||||
if(_method == ITEM_PARRY)
|
||||
using_item = thing
|
||||
tool = using_item
|
||||
method = ITEM_PARRY
|
||||
data = using_item.block_parry_data
|
||||
else if(_method == UNARMED_PARRY)
|
||||
method = UNARMED_PARRY
|
||||
tool = src
|
||||
data = thing
|
||||
if(!using_item && !method && length(other_items))
|
||||
using_item = other_items[1]
|
||||
tool = using_item
|
||||
method = ITEM_PARRY
|
||||
data = using_item.block_parry_data
|
||||
if(!method)
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
|
||||
/**
|
||||
* Returns the thing in our active hand (whatever is in our active module-slot, in this case)
|
||||
*
|
||||
* Arguments
|
||||
* * get_gripper - If the active module is a gripper, should we return the gripper or the contained item? (if the gripper contains nothing, returns the gripper anyways)
|
||||
*/
|
||||
/mob/living/silicon/robot/get_active_held_item()
|
||||
/mob/living/silicon/robot/get_active_held_item(get_gripper = FALSE)
|
||||
var/item = module_active
|
||||
// snowflake handler for the gripper
|
||||
if(istype(item, /obj/item/weapon/gripper))
|
||||
var/obj/item/weapon/gripper/G = item
|
||||
if(istype(item, /obj/item/gripper) && !get_gripper)
|
||||
var/obj/item/gripper/G = item
|
||||
if(G.wrapped)
|
||||
if(G.wrapped.loc != G)
|
||||
G.wrapped = null
|
||||
@@ -284,9 +287,14 @@
|
||||
|
||||
/**
|
||||
* Unequips the active held item, if there is one.
|
||||
*
|
||||
* Will always consider dropping gripper contents first.
|
||||
*/
|
||||
/mob/living/silicon/robot/proc/uneq_active()
|
||||
if(module_active)
|
||||
var/obj/item/gripper/gripper = get_active_held_item(TRUE)
|
||||
if(istype(gripper) && gripper.drop_held())
|
||||
return
|
||||
unequip_module_from_slot(module_active, get_selected_module())
|
||||
|
||||
/**
|
||||
@@ -302,11 +310,12 @@
|
||||
* Checks if the item is currently in a slot.
|
||||
*
|
||||
* If the item is found in a slot, this returns TRUE. Otherwise, it returns FALSE
|
||||
* Modified to accept items inside of grippers, used for `code\modules\tgui\states\hands.dm:27`
|
||||
* Arguments
|
||||
* * item_module - the item being checked
|
||||
*/
|
||||
/mob/living/silicon/robot/proc/activated(obj/item/item_module)
|
||||
if(item_module in held_items)
|
||||
if(get_active_held_item() == item_module || (item_module in held_items))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -328,7 +328,7 @@
|
||||
/obj/item/crowbar/cyborg,
|
||||
/obj/item/healthanalyzer,
|
||||
/obj/item/reagent_containers/borghypo,
|
||||
/obj/item/weapon/gripper/medical,
|
||||
/obj/item/gripper/medical,
|
||||
/obj/item/reagent_containers/dropper,
|
||||
/obj/item/reagent_containers/syringe,
|
||||
/obj/item/surgical_drapes,
|
||||
@@ -448,7 +448,7 @@
|
||||
/obj/item/analyzer,
|
||||
/obj/item/storage/part_replacer/cyborg,
|
||||
/obj/item/holosign_creator/combifan,
|
||||
/obj/item/weapon/gripper,
|
||||
/obj/item/gripper,
|
||||
/obj/item/lightreplacer/cyborg,
|
||||
/obj/item/geiger_counter/cyborg,
|
||||
/obj/item/assembly/signaler/cyborg,
|
||||
@@ -923,7 +923,7 @@
|
||||
/obj/item/gun/energy/kinetic_accelerator/cyborg,
|
||||
/obj/item/gun/energy/plasmacutter/cyborg,
|
||||
/obj/item/gps/cyborg,
|
||||
/obj/item/weapon/gripper/mining,
|
||||
/obj/item/gripper/mining,
|
||||
/obj/item/cyborg_clamp,
|
||||
/obj/item/stack/marker_beacon,
|
||||
/obj/item/destTagger,
|
||||
@@ -1075,7 +1075,7 @@
|
||||
/obj/item/multitool/cyborg,
|
||||
/obj/item/storage/part_replacer/cyborg,
|
||||
/obj/item/holosign_creator/atmos,
|
||||
/obj/item/weapon/gripper,
|
||||
/obj/item/gripper,
|
||||
/obj/item/lightreplacer/cyborg,
|
||||
/obj/item/stack/sheet/metal/cyborg,
|
||||
/obj/item/stack/sheet/glass/cyborg,
|
||||
|
||||
@@ -147,6 +147,7 @@ GLOBAL_LIST(bad_gremlin_items)
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/death(gibbed)
|
||||
walk(src,0)
|
||||
QDEL_NULL(access_card)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/gremlin/Life()
|
||||
|
||||
@@ -528,21 +528,7 @@ Difficulty: Very Hard
|
||||
/obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method)
|
||||
if(..())
|
||||
var/obj/item/projectile/P = new generated_projectile(get_turf(src))
|
||||
P.setDir(dir)
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
P.yo = 20
|
||||
P.xo = 0
|
||||
if(EAST)
|
||||
P.yo = 0
|
||||
P.xo = 20
|
||||
if(WEST)
|
||||
P.yo = 0
|
||||
P.xo = -20
|
||||
else
|
||||
P.yo = -20
|
||||
P.xo = 0
|
||||
P.fire()
|
||||
P.fire(angle2dir(dir))
|
||||
|
||||
/obj/machinery/anomalous_crystal/dark_reprise //Revives anyone nearby, but turns them into shadowpeople and renders them uncloneable, so the crystal is your only hope of getting up again if you go down.
|
||||
observer_desc = "When activated, this crystal revives anyone nearby, but turns them into Shadowpeople and makes them unclonable, making the crystal their only hope of getting up again."
|
||||
|
||||
@@ -32,7 +32,7 @@ Key procs
|
||||
/// Unique ID. You can never have different modifications with the same ID. By default, this SHOULD NOT be set. Only set it for cases where you're dynamically making modifiers/need to have two types overwrite each other. If unset, uses path (converted to text) as ID.
|
||||
var/id
|
||||
|
||||
/// Higher ones override lower priorities. This is NOT used for ID, ID must be unique, if it isn't unique the newer one overwrites automatically if overriding.
|
||||
/// Determines order. Lower priorities are applied first.
|
||||
var/priority = 0
|
||||
var/flags = NONE
|
||||
|
||||
@@ -66,8 +66,10 @@ Key procs
|
||||
if(!complex_calculation || (multiplicative_slowdown > 0)) // we aren't limiting how much things can slowdown.. yet.
|
||||
return existing + multiplicative_slowdown
|
||||
var/current_tiles = 10 / max(existing, world.tick_lag)
|
||||
var/minimum_speed = 10 / min(current_tiles + max_tiles_per_second_boost, max(current_tiles, absolute_max_tiles_per_second))
|
||||
return max(minimum_speed, existing + multiplicative_slowdown)
|
||||
// multiplicative_slowdown is negative due to our first check
|
||||
var/max_buff_to = max(existing + multiplicative_slowdown, 10 / absolute_max_tiles_per_second, 10 / (current_tiles + max_tiles_per_second_boost))
|
||||
// never slow the user
|
||||
return min(existing, max_buff_to)
|
||||
|
||||
GLOBAL_LIST_EMPTY(movespeed_modification_cache)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/datum/movespeed_modifier/reagent/ephedrine
|
||||
// strong painkiller effect that caps out at slightly above runspeed
|
||||
multiplicative_slowdown = -1.5
|
||||
priority = -100
|
||||
priority = 500
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 7
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
// extremely strong painkiller effect: allows user to run at old sprint speeds but not over by cancelling out slowdowns.
|
||||
// however, will not make user go faster than that
|
||||
multiplicative_slowdown = -4
|
||||
priority = -100
|
||||
priority = 500
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 8
|
||||
|
||||
/datum/movespeed_modifier/reagent/methamphetamine
|
||||
// very strong painkiller effect that caps out at slightly above runspeed
|
||||
multiplicative_slowdown = -2.5
|
||||
priority = -100
|
||||
priority = 500
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 7.5
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
/datum/movespeed_modifier/status_effect/tased
|
||||
multiplicative_slowdown = 1.5
|
||||
priority = 50
|
||||
priority = 1500
|
||||
|
||||
/datum/movespeed_modifier/status_effect/domain
|
||||
multiplicative_slowdown = 3
|
||||
|
||||
/datum/movespeed_modifier/status_effect/tased/no_combat_mode
|
||||
multiplicative_slowdown = 8
|
||||
priority = 100
|
||||
priority = 1500
|
||||
|
||||
/datum/movespeed_modifier/status_effect/electrostaff
|
||||
multiplicative_slowdown = 1
|
||||
@@ -55,7 +55,7 @@
|
||||
/datum/movespeed_modifier/status_effect/slime/light_pink
|
||||
// decently good painkiller + speedup effect
|
||||
blacklisted_movetypes = FLYING | FLOATING
|
||||
priority = -150 // someday we really need to make these defines lmao
|
||||
priority = 500 // someday we really need to make these defines lmao
|
||||
multiplicative_slowdown = -2
|
||||
complex_calculation = TRUE
|
||||
absolute_max_tiles_per_second = 7
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
obj_flags = CAN_BE_HIT | UNIQUE_RENAME
|
||||
circuit = /obj/item/circuitboard/machine/hydroponics/automagic
|
||||
|
||||
|
||||
/obj/machinery/hydroponics/constructable/automagic/attackby(obj/item/O, mob/user, params)
|
||||
if(istype(O, /obj/item/reagent_containers))
|
||||
return FALSE //avoid fucky wuckies
|
||||
..()
|
||||
|
||||
/obj/machinery/hydroponics/constructable/automagic/default_unfasten_wrench(mob/user, obj/item/I, time = 20)
|
||||
. = ..()
|
||||
if(. == SUCCESSFUL_UNFASTEN)
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
var/fermiIsReacting = FALSE //that prevents multiple reactions from occurring (i.e. add_reagent calls to process_reactions(), this stops any extra reactions.)
|
||||
var/fermiReactID //instance of the chem reaction used during a fermireaction, kept here so it's cache isn't lost between loops/procs.
|
||||
var/value_multiplier = DEFAULT_REAGENTS_VALUE //used for cargo reagents selling.
|
||||
var/force_alt_taste = FALSE
|
||||
|
||||
/datum/reagents/New(maximum=100, new_flags = NONE, new_value = DEFAULT_REAGENTS_VALUE)
|
||||
maximum_volume = maximum
|
||||
@@ -1120,47 +1121,54 @@
|
||||
. = locate(type) in cached_reagents
|
||||
|
||||
/datum/reagents/proc/generate_taste_message(minimum_percent=15)
|
||||
// the lower the minimum percent, the more sensitive the message is.
|
||||
var/list/out = list()
|
||||
var/list/tastes = list() //descriptor = strength
|
||||
if(minimum_percent <= 100)
|
||||
for(var/datum/reagent/R in reagent_list)
|
||||
if(!R.taste_mult)
|
||||
continue
|
||||
|
||||
if(istype(R, /datum/reagent/consumable/nutriment))
|
||||
var/list/taste_data = R.data
|
||||
for(var/taste in taste_data)
|
||||
var/ratio = taste_data[taste]
|
||||
var/amount = ratio * R.taste_mult * R.volume
|
||||
if(taste in tastes)
|
||||
tastes[taste] += amount
|
||||
else
|
||||
tastes[taste] = amount
|
||||
else
|
||||
var/taste_desc = R.taste_description
|
||||
var/taste_amount = R.volume * R.taste_mult
|
||||
if(taste_desc in tastes)
|
||||
tastes[taste_desc] += taste_amount
|
||||
else
|
||||
tastes[taste_desc] = taste_amount
|
||||
//deal with percentages
|
||||
// TODO it would be great if we could sort these from strong to weak
|
||||
var/total_taste = counterlist_sum(tastes)
|
||||
if(total_taste > 0)
|
||||
for(var/taste_desc in tastes)
|
||||
var/percent = tastes[taste_desc]/total_taste * 100
|
||||
if(percent < minimum_percent)
|
||||
if(!force_alt_taste)
|
||||
// the lower the minimum percent, the more sensitive the message is.
|
||||
var/list/tastes = list() //descriptor = strength
|
||||
if(minimum_percent <= 100)
|
||||
for(var/datum/reagent/R in reagent_list)
|
||||
if(!R.taste_mult)
|
||||
continue
|
||||
var/intensity_desc = "a hint of"
|
||||
if(ISINRANGE(percent, minimum_percent * 2, minimum_percent * 3)|| percent == 100)
|
||||
intensity_desc = ""
|
||||
else if(percent > minimum_percent * 3)
|
||||
intensity_desc = "the strong flavor of"
|
||||
if(intensity_desc != "")
|
||||
out += "[intensity_desc] [taste_desc]"
|
||||
|
||||
if(istype(R, /datum/reagent/consumable/nutriment))
|
||||
var/list/taste_data = R.data
|
||||
for(var/taste in taste_data)
|
||||
var/ratio = taste_data[taste]
|
||||
var/amount = ratio * R.taste_mult * R.volume
|
||||
if(taste in tastes)
|
||||
tastes[taste] += amount
|
||||
else
|
||||
tastes[taste] = amount
|
||||
else
|
||||
out += "[taste_desc]"
|
||||
var/taste_desc = R.taste_description
|
||||
var/taste_amount = R.volume * R.taste_mult
|
||||
if(taste_desc in tastes)
|
||||
tastes[taste_desc] += taste_amount
|
||||
else
|
||||
tastes[taste_desc] = taste_amount
|
||||
//deal with percentages
|
||||
// TODO it would be great if we could sort these from strong to weak
|
||||
var/total_taste = counterlist_sum(tastes)
|
||||
if(total_taste > 0)
|
||||
for(var/taste_desc in tastes)
|
||||
var/percent = tastes[taste_desc]/total_taste * 100
|
||||
if(percent < minimum_percent)
|
||||
continue
|
||||
var/intensity_desc = "a hint of"
|
||||
if(ISINRANGE(percent, minimum_percent * 2, minimum_percent * 3)|| percent == 100)
|
||||
intensity_desc = ""
|
||||
else if(percent > minimum_percent * 3)
|
||||
intensity_desc = "the strong flavor of"
|
||||
if(intensity_desc != "")
|
||||
out += "[intensity_desc] [taste_desc]"
|
||||
else
|
||||
out += "[taste_desc]"
|
||||
|
||||
else
|
||||
// alternate taste is to force the taste of the atom if its a food item
|
||||
if(my_atom && isfood(my_atom))
|
||||
var/obj/item/reagent_containers/food/snacks/F = my_atom
|
||||
out = F.tastes
|
||||
|
||||
return english_list(out, "something indescribable")
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
name = "combat stimulant injector"
|
||||
desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat and get people back in the fight."
|
||||
amount_per_transfer_from_this = 10
|
||||
item_state = "combat_hypo"
|
||||
icon_state = "combat_hypo"
|
||||
volume = 100
|
||||
ignore_flags = 1 // So they can heal their comrades.
|
||||
@@ -69,17 +70,27 @@
|
||||
list_reagents = list(/datum/reagent/medicine/epinephrine = 30, /datum/reagent/medicine/omnizine = 30, /datum/reagent/medicine/leporazine = 15, /datum/reagent/medicine/atropine = 15)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/combat/nanites
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with experimental medical compounds for rapid healing."
|
||||
name = "experimental combat stimulant injector"
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with experimental medical nanites and a stimulant for rapid healing and a combat boost."
|
||||
item_state = "nanite_hypo"
|
||||
icon_state = "nanite_hypo"
|
||||
volume = 100
|
||||
list_reagents = list(/datum/reagent/medicine/adminordrazine/quantum_heal = 80, /datum/reagent/medicine/synaptizine = 20)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/magillitis
|
||||
name = "experimental autoinjector"
|
||||
desc = "A modified air-needle autoinjector with a small single-use reservoir. It contains an experimental serum."
|
||||
icon_state = "combat_hypo"
|
||||
volume = 5
|
||||
reagent_flags = NONE
|
||||
list_reagents = list(/datum/reagent/magillitis = 5)
|
||||
/obj/item/reagent_containers/hypospray/combat/nanites/update_icon()
|
||||
if(reagents.total_volume > 0)
|
||||
icon_state = initial(icon_state)
|
||||
else
|
||||
icon_state = "[initial(icon_state)]0"
|
||||
|
||||
/obj/item/reagent_containers/hypospray/combat/heresypurge
|
||||
name = "holy water piercing injector"
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with 5 doses of a holy water and pacifier mixture. Not for use on your teammates."
|
||||
item_state = "holy_hypo"
|
||||
icon_state = "holy_hypo"
|
||||
volume = 250
|
||||
list_reagents = list(/datum/reagent/water/holywater = 150, /datum/reagent/peaceborg_tire = 50, /datum/reagent/peaceborg_confuse = 50)
|
||||
amount_per_transfer_from_this = 50
|
||||
|
||||
//MediPens
|
||||
|
||||
@@ -136,6 +147,8 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/ekit
|
||||
name = "emergency first-aid autoinjector"
|
||||
desc = "An epinephrine medipen with extra coagulant and antibiotics to help stabilize bad cuts and burns."
|
||||
icon_state = "firstaid"
|
||||
item_state = "firstaid"
|
||||
volume = 15
|
||||
amount_per_transfer_from_this = 15
|
||||
list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/spaceacillin = 0.5)
|
||||
@@ -143,15 +156,19 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/blood_loss
|
||||
name = "hypovolemic-response autoinjector"
|
||||
desc = "A medipen designed to stabilize and rapidly reverse severe bloodloss."
|
||||
icon_state = "hypovolemic"
|
||||
item_state = "hypovolemic"
|
||||
volume = 15
|
||||
amount_per_transfer_from_this = 15
|
||||
list_reagents = list(/datum/reagent/medicine/epinephrine = 5, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/iron = 3.5, /datum/reagent/medicine/salglu_solution = 4)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/stimulants
|
||||
name = "illegal stimpack medipen"
|
||||
desc = "A highly illegal medipen due to its load and small injections, allow for five uses before being drained"
|
||||
name = "stimpack medipen"
|
||||
desc = "Contains stimulants."
|
||||
icon_state = "syndipen"
|
||||
item_state = "syndipen"
|
||||
volume = 50
|
||||
amount_per_transfer_from_this = 10
|
||||
amount_per_transfer_from_this = 50
|
||||
list_reagents = list(/datum/reagent/medicine/stimulants = 50)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/stimulants/baseball
|
||||
@@ -166,6 +183,7 @@
|
||||
name = "stimpack medipen"
|
||||
desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor."
|
||||
icon_state = "stimpen"
|
||||
item_state = "stimpen"
|
||||
volume = 20
|
||||
amount_per_transfer_from_this = 20
|
||||
list_reagents = list(/datum/reagent/medicine/ephedrine = 10, /datum/reagent/consumable/coffee = 10)
|
||||
@@ -177,20 +195,79 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/morphine
|
||||
name = "morphine medipen"
|
||||
desc = "A rapid way to get you out of a tight situation and fast! You'll feel rather drowsy, though."
|
||||
icon_state = "morphen"
|
||||
item_state = "morphen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/morphine = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/penacid
|
||||
name = "pentetic acid medipen"
|
||||
desc = "A autoinjector containing pentetic acid, used to reduce high levels of radiations and moderate toxins."
|
||||
icon_state = "penacid"
|
||||
item_state = "penacid"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/pen_acid = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/atropine
|
||||
name = "atropine autoinjector"
|
||||
desc = "A rapid way to save a person from a critical injury state!"
|
||||
icon_state = "atropen"
|
||||
item_state = "atropen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/atropine = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/salacid
|
||||
name = "salicyclic acid medipen"
|
||||
desc = "A autoinjector containing salicyclic acid, used to treat severe brute damage."
|
||||
icon_state = "salacid"
|
||||
item_state = "salacid"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/sal_acid = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/oxandrolone
|
||||
name = "oxandrolone medipen"
|
||||
desc = "A autoinjector containing oxandrolone, used to treat severe burns."
|
||||
icon_state = "oxapen"
|
||||
item_state = "oxapen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/oxandrolone = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/salbutamol
|
||||
name = "salbutamol medipen"
|
||||
desc = "A autoinjector containing salbutamol, used to heal oxygen damage quickly."
|
||||
icon_state = "salpen"
|
||||
item_state = "salpen"
|
||||
volume = 10
|
||||
amount_per_transfer_from_this = 10
|
||||
list_reagents = list(/datum/reagent/medicine/salbutamol = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure
|
||||
name = "BVAK autoinjector"
|
||||
desc = "Bio Virus Antidote Kit autoinjector. Has a two use system for yourself, and someone else. Inject when infected."
|
||||
icon_state = "stimpen"
|
||||
icon_state = "tbpen"
|
||||
item_state = "tbpen"
|
||||
volume = 60
|
||||
amount_per_transfer_from_this = 30
|
||||
list_reagents = list(/datum/reagent/medicine/atropine = 10, /datum/reagent/medicine/epinephrine = 10, /datum/reagent/medicine/salbutamol = 20, /datum/reagent/medicine/spaceacillin = 20)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure/update_icon()
|
||||
if(reagents.total_volume > 30)
|
||||
icon_state = initial(icon_state)
|
||||
else if (reagents.total_volume > 0)
|
||||
icon_state = "[initial(icon_state)]1"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]0"
|
||||
|
||||
/obj/item/reagent_containers/hypospray/medipen/survival
|
||||
name = "survival medipen"
|
||||
desc = "A medipen for surviving in the harshest of environments, heals and protects from environmental hazards. WARNING: Do not inject more than one pen in quick succession."
|
||||
icon_state = "stimpen"
|
||||
icon_state = "minepen"
|
||||
item_state = "minepen"
|
||||
volume = 52
|
||||
amount_per_transfer_from_this = 52
|
||||
list_reagents = list(/datum/reagent/medicine/salbutamol = 10, /datum/reagent/medicine/leporazine = 15, /datum/reagent/medicine/neo_jelly = 15, /datum/reagent/medicine/epinephrine = 10, /datum/reagent/medicine/lavaland_extract = 2)
|
||||
@@ -198,16 +275,21 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen/firelocker
|
||||
name = "fire treatment medipen"
|
||||
desc = "A medipen that has been fulled with burn healing chemicals for personnel without advanced medical knowledge."
|
||||
icon_state = "firepen"
|
||||
item_state = "firepen"
|
||||
volume = 15
|
||||
amount_per_transfer_from_this = 15
|
||||
list_reagents = list(/datum/reagent/medicine/oxandrolone = 5, /datum/reagent/medicine/kelotane = 10)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/combat/heresypurge
|
||||
name = "holy water autoinjector"
|
||||
desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with 5 doses of a holy water mixture."
|
||||
volume = 250
|
||||
list_reagents = list(/datum/reagent/water/holywater = 150, /datum/reagent/peaceborg_tire = 50, /datum/reagent/peaceborg_confuse = 50)
|
||||
amount_per_transfer_from_this = 50
|
||||
/obj/item/reagent_containers/hypospray/medipen/magillitis
|
||||
name = "experimental autoinjector"
|
||||
desc = "A custom-frame needle injector with a small single-use reservoir, containing an experimental serum. Unlike the more common medipen frame, it cannot pierce through protective armor or hardsuits, nor can the chemical inside be extracted."
|
||||
icon_state = "gorillapen"
|
||||
item_state = "gorillapen"
|
||||
volume = 5
|
||||
ignore_flags = 0
|
||||
reagent_flags = NONE
|
||||
list_reagents = list(/datum/reagent/magillitis = 5)
|
||||
|
||||
#define HYPO_SPRAY 0
|
||||
#define HYPO_INJECT 1
|
||||
|
||||
@@ -63,10 +63,10 @@
|
||||
name = "DNA Sampler"
|
||||
desc = "Can be used to take chemical and genetic samples of pretty much anything."
|
||||
icon = 'icons/obj/syringe.dmi'
|
||||
item_state = "hypo"
|
||||
item_state = "sampler"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
|
||||
icon_state = "hypo"
|
||||
icon_state = "sampler"
|
||||
item_flags = NOBLUDGEON
|
||||
var/list/animals = list()
|
||||
var/list/plants = list()
|
||||
|
||||
@@ -32,47 +32,79 @@
|
||||
/// Intended to be used in the manner of `TEST_FOCUS(/datum/unit_test/math)`
|
||||
#define TEST_FOCUS(test_path) ##test_path { focus = TRUE; }
|
||||
|
||||
/// Constants indicating unit test completion status
|
||||
#define UNIT_TEST_PASSED 0
|
||||
#define UNIT_TEST_FAILED 1
|
||||
#define UNIT_TEST_SKIPPED 2
|
||||
|
||||
#define TEST_DEFAULT 1
|
||||
#define TEST_DEL_WORLD INFINITY
|
||||
|
||||
/// A trait source when adding traits through unit tests
|
||||
#define TRAIT_SOURCE_UNIT_TESTS "unit_tests"
|
||||
|
||||
#include "anchored_mobs.dm"
|
||||
#include "bespoke_id.dm"
|
||||
#include "binary_insert.dm"
|
||||
// #include "bloody_footprints.dm"
|
||||
// #include "breath.dm"
|
||||
// #include "card_mismatch.dm"
|
||||
#include "chain_pull_through_space.dm"
|
||||
// #include "combat.dm"
|
||||
#include "component_tests.dm"
|
||||
// #include "connect_loc.dm"
|
||||
// #include "confusion.dm"
|
||||
// #include "crayons.dm"
|
||||
// #include "create_and_destroy.dm"
|
||||
// #include "designs.dm"
|
||||
// #include "dynamic_ruleset_sanity.dm"
|
||||
// #include "egg_glands.dm"
|
||||
// #include "emoting.dm"
|
||||
// #include "food_edibility_check.dm"
|
||||
// #include "greyscale_config.dm"
|
||||
// #include "heretic_knowledge.dm"
|
||||
// #include "holidays.dm"
|
||||
#include "initialize_sanity.dm"
|
||||
// #include "hydroponics_harvest.dm"
|
||||
// #include "keybinding_init.dm"
|
||||
#include "machine_disassembly.dm"
|
||||
#include "medical_wounds.dm"
|
||||
#include "merge_type.dm"
|
||||
// #include "metabolizing.dm"
|
||||
// #include "ntnetwork_tests.dm"
|
||||
// #include "outfit_sanity.dm"
|
||||
// #include "pills.dm"
|
||||
// #include "plantgrowth_tests.dm"
|
||||
// #include "projectiles.dm"
|
||||
// #include "rcd.dm"
|
||||
#include "reagent_id_typos.dm"
|
||||
// #include "reagent_mod_expose.dm"
|
||||
// #include "reagent_mod_procs.dm"
|
||||
#include "reagent_recipe_collisions.dm"
|
||||
#include "resist.dm"
|
||||
// #include "say.dm"
|
||||
// #include "security_officer_distribution.dm"
|
||||
// #include "serving_tray.dm"
|
||||
// #include "siunit.dm"
|
||||
#include "spawn_humans.dm"
|
||||
#include "spawn_mobs.dm"
|
||||
// #include "species_whitelists.dm"
|
||||
// #include "stomach.dm"
|
||||
// #include "strippable.dm"
|
||||
#include "subsystem_init.dm"
|
||||
#include "surgeries.dm"
|
||||
#include "teleporters.dm"
|
||||
#include "tgui_create_message.dm"
|
||||
#include "timer_sanity.dm"
|
||||
#include "unit_test.dm"
|
||||
// #include "wizard.dm"
|
||||
|
||||
/// CIT TESTS
|
||||
#include "character_saving.dm"
|
||||
|
||||
#ifdef REFERENCE_TRACKING //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
|
||||
#include "find_reference_sanity.dm"
|
||||
#endif
|
||||
|
||||
#undef TEST_ASSERT
|
||||
#undef TEST_ASSERT_EQUAL
|
||||
#undef TEST_ASSERT_NOTEQUAL
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/datum/unit_test/chain_pull_through_space
|
||||
var/turf/open/space/space_tile
|
||||
var/turf/claimed_tile
|
||||
var/claimed_tile
|
||||
var/mob/living/carbon/human/alice
|
||||
var/mob/living/carbon/human/bob
|
||||
var/mob/living/carbon/human/charlie
|
||||
@@ -9,25 +9,25 @@
|
||||
..()
|
||||
|
||||
// Create a space tile that goes to another z-level
|
||||
claimed_tile = run_loc_bottom_left
|
||||
claimed_tile = run_loc_floor_bottom_left.type
|
||||
|
||||
space_tile = new(locate(run_loc_bottom_left.x, run_loc_bottom_left.y, run_loc_bottom_left.z))
|
||||
space_tile = new(locate(run_loc_floor_bottom_left.x, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
|
||||
space_tile.destination_x = 100
|
||||
space_tile.destination_y = 100
|
||||
space_tile.destination_z = 5
|
||||
|
||||
// Create our list of humans, all adjacent to one another
|
||||
alice = new(locate(run_loc_bottom_left.x + 2, run_loc_bottom_left.y, run_loc_bottom_left.z))
|
||||
alice = new(locate(run_loc_floor_bottom_left.x + 2, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
|
||||
alice.name = "Alice"
|
||||
|
||||
bob = new(locate(run_loc_bottom_left.x + 3, run_loc_bottom_left.y, run_loc_bottom_left.z))
|
||||
bob = new(locate(run_loc_floor_bottom_left.x + 3, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
|
||||
bob.name = "Bob"
|
||||
|
||||
charlie = new(locate(run_loc_bottom_left.x + 4, run_loc_bottom_left.y, run_loc_bottom_left.z))
|
||||
charlie = new(locate(run_loc_floor_bottom_left.x + 4, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
|
||||
charlie.name = "Charlie"
|
||||
|
||||
/datum/unit_test/chain_pull_through_space/Destroy()
|
||||
space_tile.copyTurf(claimed_tile)
|
||||
space_tile.ChangeTurf(claimed_tile)
|
||||
qdel(alice)
|
||||
qdel(bob)
|
||||
qdel(charlie)
|
||||
@@ -40,14 +40,14 @@
|
||||
bob.start_pulling(charlie)
|
||||
|
||||
// Walk normally to the left, make sure we're still a chain
|
||||
alice.Move(locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z))
|
||||
if (bob.x != run_loc_bottom_left.x + 2)
|
||||
alice.Move(locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
|
||||
if (bob.x != run_loc_floor_bottom_left.x + 2)
|
||||
return Fail("During normal move, Bob was not at the correct x ([bob.x])")
|
||||
if (charlie.x != run_loc_bottom_left.x + 3)
|
||||
if (charlie.x != run_loc_floor_bottom_left.x + 3)
|
||||
return Fail("During normal move, Charlie was not at the correct x ([charlie.x])")
|
||||
|
||||
// We're going through the space turf now that should teleport us
|
||||
alice.Move(run_loc_bottom_left)
|
||||
alice.Move(run_loc_floor_bottom_left)
|
||||
if (alice.z != space_tile.destination_z)
|
||||
return Fail("Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])")
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
///Used to test the completeness of the reference finder proc.
|
||||
/datum/unit_test/find_reference_sanity
|
||||
|
||||
/atom/movable/ref_holder
|
||||
var/atom/movable/ref_test/test
|
||||
var/list/test_list = list()
|
||||
var/list/test_assoc_list = list()
|
||||
|
||||
/atom/movable/ref_holder/Destroy()
|
||||
test = null
|
||||
test_list.Cut()
|
||||
test_assoc_list.Cut()
|
||||
return ..()
|
||||
|
||||
/atom/movable/ref_test
|
||||
var/atom/movable/ref_test/self_ref
|
||||
|
||||
/atom/movable/ref_test/Destroy(force)
|
||||
self_ref = null
|
||||
return ..()
|
||||
|
||||
/datum/unit_test/find_reference_sanity/Run()
|
||||
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
|
||||
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Sanity check
|
||||
victim.DoSearchVar(testbed, "Sanity Check", search_time = 1) //We increment search time to get around an optimization
|
||||
TEST_ASSERT(!victim.found_refs.len, "The ref-tracking tool found a ref where none existed")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
|
||||
/datum/unit_test/find_reference_baseline/Run()
|
||||
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
|
||||
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Set up for the first round of tests
|
||||
testbed.test = victim
|
||||
testbed.test_list += victim
|
||||
testbed.test_assoc_list["baseline"] = victim
|
||||
|
||||
victim.DoSearchVar(testbed, "First Run", search_time = 2)
|
||||
|
||||
TEST_ASSERT(victim.found_refs["test"], "The ref-tracking tool failed to find a regular value")
|
||||
TEST_ASSERT(victim.found_refs[testbed.test_list], "The ref-tracking tool failed to find a list entry")
|
||||
TEST_ASSERT(victim.found_refs[testbed.test_assoc_list], "The ref-tracking tool failed to find an assoc list value")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
|
||||
/datum/unit_test/find_reference_exotic/Run()
|
||||
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
|
||||
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Second round, bit harder this time
|
||||
testbed.overlays += victim
|
||||
testbed.vis_contents += victim
|
||||
testbed.test_assoc_list[victim] = TRUE
|
||||
|
||||
victim.DoSearchVar(testbed, "Second Run", search_time = 3)
|
||||
|
||||
//This is another sanity check
|
||||
TEST_ASSERT(!victim.found_refs[testbed.overlays], "The ref-tracking tool found an overlays entry? That shouldn't be possible")
|
||||
TEST_ASSERT(victim.found_refs[testbed.vis_contents], "The ref-tracking tool failed to find a vis_contents entry")
|
||||
TEST_ASSERT(victim.found_refs[testbed.test_assoc_list], "The ref-tracking tool failed to find an assoc list key")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
|
||||
/datum/unit_test/find_reference_esoteric/Run()
|
||||
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
|
||||
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Let's get a bit esoteric
|
||||
victim.self_ref = victim
|
||||
var/list/to_find = list(victim)
|
||||
testbed.test_list += list(to_find)
|
||||
var/list/to_find_assoc = list(victim)
|
||||
testbed.test_assoc_list["Nesting"] = to_find_assoc
|
||||
|
||||
victim.DoSearchVar(victim, "Third Run Self", search_time = 4)
|
||||
victim.DoSearchVar(testbed, "Third Run Testbed", search_time = 4)
|
||||
TEST_ASSERT(victim.found_refs["self_ref"], "The ref-tracking tool failed to find a self reference")
|
||||
TEST_ASSERT(victim.found_refs[to_find], "The ref-tracking tool failed to find a nested list entry")
|
||||
TEST_ASSERT(victim.found_refs[to_find_assoc], "The ref-tracking tool failed to find a nested assoc list entry")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
|
||||
/datum/unit_test/find_reference_null_key_entry/Run()
|
||||
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
|
||||
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Calm before the storm
|
||||
testbed.test_assoc_list = list(null = victim)
|
||||
|
||||
victim.DoSearchVar(testbed, "Fourth Run", search_time = 5)
|
||||
TEST_ASSERT(testbed.test_assoc_list, "The ref-tracking tool failed to find a null key'd assoc list entry")
|
||||
|
||||
/datum/unit_test/find_reference_assoc_investigation/Run()
|
||||
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
|
||||
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Let's do some more complex assoc list investigation
|
||||
var/list/to_find_in_key = list(victim)
|
||||
testbed.test_assoc_list[to_find_in_key] = list("memes")
|
||||
var/list/to_find_null_assoc_nested = list(victim)
|
||||
testbed.test_assoc_list[null] = to_find_null_assoc_nested
|
||||
|
||||
victim.DoSearchVar(testbed, "Fifth Run", search_time = 6)
|
||||
TEST_ASSERT(victim.found_refs[to_find_in_key], "The ref-tracking tool failed to find a nested assoc list key")
|
||||
TEST_ASSERT(victim.found_refs[to_find_null_assoc_nested], "The ref-tracking tool failed to find a null key'd nested assoc list entry")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
@@ -1,11 +0,0 @@
|
||||
/datum/unit_test/initialize_sanity/Run()
|
||||
if(length(SSatoms.BadInitializeCalls))
|
||||
Fail("Bad Initialize() calls detected. Please read logs.")
|
||||
var/list/init_failures_to_text = list(
|
||||
"[BAD_INIT_QDEL_BEFORE]" = "Qdeleted Before Initialized",
|
||||
"[BAD_INIT_DIDNT_INIT]" = "Did Not Initialize",
|
||||
"[BAD_INIT_SLEPT]" = "Initialize() Slept",
|
||||
"[BAD_INIT_NO_HINT]" = "No Initialize() Hint Returned",
|
||||
)
|
||||
for(var/failure in SSatoms.BadInitializeCalls)
|
||||
log_world("[failure]: [init_failures_to_text["[SSatoms.BadInitializeCalls[failure]]"]]") // You like stacked brackets?
|
||||
@@ -1,23 +0,0 @@
|
||||
/// Test to verify message mods are parsed correctly
|
||||
/datum/unit_test/get_message_mods
|
||||
var/mob/host_mob
|
||||
|
||||
/datum/unit_test/get_message_mods/Run()
|
||||
host_mob = allocate(/mob/living/carbon/human)
|
||||
|
||||
test("Hello", "Hello", list())
|
||||
test(";HELP", "HELP", list(MODE_HEADSET = TRUE))
|
||||
test(";%Never gonna give you up", "Never gonna give you up", list(MODE_HEADSET = TRUE, MODE_SING = TRUE))
|
||||
test(".s Gun plz", "Gun plz", list(RADIO_KEY = RADIO_KEY_SECURITY, RADIO_EXTENSION = RADIO_CHANNEL_SECURITY))
|
||||
test("...What", "...What", list())
|
||||
|
||||
/datum/unit_test/get_message_mods/proc/test(message, expected_message, list/expected_mods)
|
||||
var/list/mods = list()
|
||||
TEST_ASSERT_EQUAL(host_mob.get_message_mods(message, mods), expected_message, "Chopped message was not what we expected. Message: [message]")
|
||||
|
||||
for (var/mod_key in mods)
|
||||
TEST_ASSERT_EQUAL(mods[mod_key], expected_mods[mod_key], "The value for [mod_key] was not what we expected. Message: [message]")
|
||||
expected_mods -= mod_key
|
||||
|
||||
if (expected_mods.len)
|
||||
Fail("Some message mods were expected, but were not returned by get_message_mods: [json_encode(expected_mods)]. Message: [message]")
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/unit_test/spawn_humans/Run()
|
||||
var/locs = block(run_loc_bottom_left, run_loc_top_right)
|
||||
var/locs = block(run_loc_floor_bottom_left, run_loc_floor_top_right)
|
||||
|
||||
for(var/I in 1 to 5)
|
||||
new /mob/living/carbon/human(pick(locs))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user