This commit is contained in:
zerothebigboy
2021-09-10 12:12:57 -04:00
211 changed files with 9352 additions and 4834 deletions
+42 -20
View File
@@ -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:
+2 -2
View File
@@ -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
View File
@@ -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
+3 -2
View File
@@ -1,2 +1,3 @@
@call tools\build\build
@pause
@echo off
call "%~dp0\tools\build\build.bat" %*
pause
+3
View File
@@ -0,0 +1,3 @@
@echo off
call "%~dp0\tools\build\build.bat" dist-clean
pause
+9 -1
View File
@@ -1,7 +1,7 @@
## Citadel Station 13
Based and maintained from /tg/station.
[![Build Status](https://api.travis-ci.org/Citadel-Station-13/Citadel-Station-13.png)](https://travis-ci.org/Citadel-Station-13/Citadel-Station-13)
[![Build Status](https://github.com/Citadel-Station-13/Citadel-Station-13/workflows/CI%20Suite/badge.svg)](https://github.com/Citadel-Station-13/Citadel-Station-13/actions?query=workflow%3A%22CI+Suite%22)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/Citadel-Station-13/Citadel-Station-13.svg)](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Percentage of issues still open")
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/Citadel-Station-13/Citadel-Station-13.svg)](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
+3
View File
@@ -0,0 +1,3 @@
@echo off
call "%~dp0\tools\build\build.bat" server %*
pause
@@ -64,10 +64,6 @@
/obj/item/paper/fluff/ruins/oldstation/protosing,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/buried_library)
"av" = (
/obj/structure/bookcase/random,
/turf/open/space/basic,
/area/ruin/unpowered/buried_library)
"aw" = (
/turf/open/floor/plating,
/area/ruin/unpowered/buried_library)
@@ -220,7 +216,6 @@
/area/ruin/unpowered/buried_library)
"bq" = (
/obj/structure/mineral_door/wood,
/obj/structure/barricade/wooden/crude/snow,
/turf/open/floor/wood,
/area/ruin/unpowered/buried_library)
"br" = (
@@ -369,7 +364,7 @@ bi
aC
ae
aQ
aq
ah
bi
aq
bg
@@ -394,7 +389,7 @@ ae
bi
aM
ae
aq
ah
bm
aq
aA
@@ -440,7 +435,7 @@ aa
ad
bi
bi
av
aq
aA
bi
aG
@@ -465,7 +460,7 @@ aa
ad
ac
as
av
aq
aF
aD
aD
@@ -550,7 +545,7 @@ aD
bi
ae
bm
ah
aq
ag
br
ac
@@ -573,9 +568,9 @@ ae
af
bp
bi
ah
aq
bi
ah
aq
bp
ac
ad
@@ -596,9 +591,9 @@ ah
ao
ah
az
ah
aq
bi
ah
aq
bi
bn
ao
@@ -621,7 +616,7 @@ ah
aA
ah
ao
ah
aq
bi
bi
bl
@@ -646,7 +641,7 @@ ah
ao
aV
bi
ah
aq
bp
bi
bm
@@ -53,8 +53,8 @@
/turf/open/floor/wood,
/area/ruin/unpowered)
"m" = (
/obj/structure/barricade/wooden/crude/snow,
/obj/structure/mineral_door/wood,
/obj/effect/decal/cleanable/trail_holder,
/turf/open/floor/wood,
/area/ruin/unpowered)
"n" = (
@@ -68,6 +68,7 @@
/obj/machinery/light/broken{
dir = 4
},
/obj/effect/decal/cleanable/trail_holder,
/turf/open/floor/wood,
/area/ruin/unpowered)
"q" = (
@@ -95,6 +96,10 @@
/obj/effect/decal/cleanable/trail_holder,
/turf/open/floor/wood,
/area/ruin/unpowered)
"N" = (
/obj/effect/decal/cleanable/trail_holder,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
(1,1,1) = {"
i
@@ -569,7 +574,7 @@ b
l
f
f
f
s
s
b
b
@@ -595,7 +600,7 @@ b
b
b
f
f
s
p
b
b
@@ -622,7 +627,7 @@ i
b
b
b
f
s
b
b
b
@@ -649,7 +654,7 @@ i
i
i
b
f
s
b
i
i
@@ -676,7 +681,7 @@ i
i
i
b
f
s
b
i
i
@@ -703,7 +708,7 @@ i
i
i
b
f
s
b
i
i
@@ -757,7 +762,7 @@ i
i
n
h
n
N
h
n
i
@@ -54557,8 +54557,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)
@@ -55493,7 +55493,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" = (
@@ -55915,6 +55915,7 @@
id = "xenoigniter";
luminosity = 2
},
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/engine,
/area/science/xenobiology)
"iPz" = (
@@ -59469,6 +59470,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" = (
@@ -60673,6 +60675,7 @@
/area/maintenance/department/science)
"tfP" = (
/obj/item/beacon,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/engine,
/area/science/xenobiology)
"tfZ" = (
@@ -114727,13 +114730,13 @@ abI
aby
aaa
xKc
blX
lCN
qAk
iPj
tfP
geN
qAk
blX
uzB
xKc
aaa
aby
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1
View File
@@ -15,6 +15,7 @@
#define BLOCK_Z_IN_UP (1<<12) // Should this object block z uprise from below?
#define SHOVABLE_ONTO (1<<13)//called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check.
#define EXAMINE_SKIP (1<<14) /// Makes the Examine proc not read out this item.
#define IN_STORAGE (1<<15) //is this item in the storage item, such as backpack? used for tooltips
/// Integrity defines for clothing (not flags but close enough)
#define CLOTHING_PRISTINE 0 // We have no damage on the clothing
+2 -2
View File
@@ -155,10 +155,10 @@
//LAVALAND
#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland
#define LAVALAND_DEFAULT_ATMOS "o2=14;n2=23;TEMP=300"
#define LAVALAND_DEFAULT_ATMOS "LAVALAND_ATMOS"
//SNOSTATION
#define ICEMOON_DEFAULT_ATMOS "o2=17;n2=63;TEMP=180"
#define ICEMOON_DEFAULT_ATMOS "ICEMOON_ATMOS"
//ATMOSIA GAS MONITOR TAGS
#define ATMOS_GAS_MONITOR_INPUT_O2 "o2_in"
+2 -1
View File
@@ -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)
+7
View File
@@ -8,6 +8,7 @@
#define FOOTSTEP_LAVA "lava"
#define FOOTSTEP_MEAT "meat"
#define FOOTSTEP_RUST "rust"
#define FOOTSTEP_CATWALK "catwalk"
//barefoot sounds
#define FOOTSTEP_WOOD_BAREFOOT "woodbarefoot"
@@ -94,6 +95,12 @@ GLOBAL_LIST_INIT(footstep, list(
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0),
FOOTSTEP_CATWALK = list(list(
'sound/effects/footstep/catwalk1.ogg',
'sound/effects/footstep/catwalk2.ogg',
'sound/effects/footstep/catwalk3.ogg',
'sound/effects/footstep/catwalk4.ogg',
'sound/effects/footstep/catwalk5.ogg'), 100, 1),
FOOTSTEP_RUST = list(list(
'sound/effects/footstep/rustystep1.ogg'), 100, 0)
))
+2 -1
View File
@@ -64,8 +64,9 @@
#define GAS_PIPE_VISIBLE_LAYER 2.47
#define GAS_FILTER_LAYER 2.48
#define GAS_PUMP_LAYER 2.49
#define LOW_OBJ_LAYER 2.5
///catwalk overlay of /turf/open/floor/plating/plating_catwalk
#define CATWALK_LAYER 2.51
#define LOW_SIGIL_LAYER 2.52
#define SIGIL_LAYER 2.54
#define HIGH_SIGIL_LAYER 2.56
+4
View File
@@ -347,3 +347,7 @@
// / Breathing types. Lungs can access either by these or by a string, which will be considered a gas ID.
#define BREATH_OXY /datum/breathing_class/oxygen
#define BREATH_PLASMA /datum/breathing_class/plasma
//Gremlins
#define NPC_TAMPER_ACT_FORGET 1 //Don't try to tamper with this again
#define NPC_TAMPER_ACT_NOMSG 2 //Don't produce a visible message
+28 -14
View File
@@ -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)
+13 -2
View File
@@ -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
+3
View File
@@ -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]")
+6
View File
@@ -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)
+4
View File
@@ -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"
+10
View File
@@ -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)
+6
View File
@@ -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
+61
View File
@@ -0,0 +1,61 @@
#define MAXIMUM_MARKOV_LENGTH 25000
/proc/markov_chain(var/text, var/order = 4, var/length = 250)
if(!text || order < 0 || order > 20 || length < 1 || length > MAXIMUM_MARKOV_LENGTH)
return
var/table = markov_table(text, order)
var/markov = markov_text(length, table, order)
return markov
/proc/markov_table(var/text, var/look_forward = 4)
if(!text)
return
var/list/table = list()
for(var/i = 1, i <= length(text), i++)
var/char = copytext(text, i, look_forward+i)
if(!table[char])
table[char] = list()
for(var/i = 1, i <= (length(text) - look_forward), i++)
var/char_index = copytext(text, i, look_forward+i)
var/char_count = copytext(text, i+look_forward, (look_forward*2)+i)
if(table[char_index][char_count])
table[char_index][char_count]++
else
table[char_index][char_count] = 1
return table
/proc/markov_text(var/length = 250, var/table, var/look_forward = 4)
if(!table)
return
var/char = pick(table)
var/o = char
for(var/i = 0, i <= (length / look_forward), i++)
var/newchar = markov_weighted_char(table[char])
if(newchar)
char = newchar
o += "[newchar]"
else
char = pick(table)
return o
/proc/markov_weighted_char(var/list/array)
if(!array || !array.len)
return
var/total = 0
for(var/i in array)
total += array[i]
var/r = rand(1, total)
for(var/i in array)
var/weight = array[i]
if(r <= weight)
return i
r -= weight
+13 -13
View File
@@ -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
View File
@@ -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
+1
View File
@@ -71,6 +71,7 @@ GLOBAL_LIST_INIT(bitfields, list(
"DROPDEL" = DROPDEL,
"NOBLUDGEON" = NOBLUDGEON,
"ABSTRACT" = ABSTRACT,
"IN_STORAGE" = IN_STORAGE,
"ITEM_CAN_BLOCK" = ITEM_CAN_BLOCK,
"ITEM_CAN_PARRY" = ITEM_CAN_PARRY
),
+1
View File
@@ -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
@@ -112,7 +112,6 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/storage/box/marshmallow = 2,
/obj/item/clothing/gloves/tackler/offbrand = 1,
/obj/item/stack/sticky_tape = 1,
/obj/effect/spawner/lootdrop/grille_or_trash = 15,
"" = 3
))
+42
View File
@@ -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
+1 -1
View File
@@ -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
+18
View File
@@ -141,11 +141,29 @@
/atom/movable/screen/inventory/MouseEntered()
..()
add_overlays()
//Apply the outline affect
add_stored_outline()
/atom/movable/screen/inventory/MouseExited()
..()
cut_overlay(object_overlays)
object_overlays.Cut()
remove_stored_outline()
/atom/movable/screen/inventory/proc/add_stored_outline()
if(hud?.mymob && slot_id)
var/obj/item/inv_item = hud.mymob.get_item_by_slot(slot_id)
if(inv_item)
if(hud?.mymob.incapacitated())
inv_item.apply_outline(COLOR_RED_GRAY)
else
inv_item.apply_outline()
/atom/movable/screen/inventory/proc/remove_stored_outline()
if(hud?.mymob && slot_id)
var/obj/item/inv_item = hud.mymob.get_item_by_slot(slot_id)
if(inv_item)
inv_item.remove_outline()
/atom/movable/screen/inventory/update_icon_state()
if(!icon_empty)
+39 -1
View File
@@ -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
+95 -14
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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.")
+43 -14
View File
@@ -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
. = ..()
+50 -4
View File
@@ -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()
@@ -37,7 +38,8 @@ SUBSYSTEM_DEF(air)
//atmos singletons
var/list/gas_reactions = list()
var/list/atmos_gen
var/list/planetary = list() //auxmos already caches static planetary mixes but could be convenient to do so here too
//Special functions lists
var/list/turf/open/high_pressure_delta = list()
@@ -53,14 +55,24 @@ 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
/datum/controller/subsystem/air/stat_entry(msg)
msg += "C:{"
@@ -218,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))
@@ -275,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--
@@ -375,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()
@@ -399,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)
@@ -473,6 +505,20 @@ SUBSYSTEM_DEF(air)
return pipe_init_dirs_cache[type]["[dir]"]
/datum/controller/subsystem/air/proc/generate_atmos()
atmos_gen = list()
for(var/T in subtypesof(/datum/atmosphere))
var/datum/atmosphere/atmostype = T
atmos_gen[initial(atmostype.id)] = new atmostype
/datum/controller/subsystem/air/proc/preprocess_gas_string(gas_string)
if(!atmos_gen)
generate_atmos()
if(!atmos_gen[gas_string])
return gas_string
var/datum/atmosphere/mix = atmos_gen[gas_string]
return mix.gas_string
#undef SSAIR_PIPENETS
#undef SSAIR_ATMOSMACHINERY
#undef SSAIR_EXCITEDGROUPS
+97 -100
View File
@@ -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
+8 -2
View File
@@ -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)
+3 -3
View File
@@ -68,7 +68,7 @@ SUBSYSTEM_DEF(vote)
//get the highest number of votes
var/greatest_votes = 0
var/total_votes = 0
if(mode == "gamemode" && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
if((mode == "gamemode" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey])
choices[choices[voted[P.ckey]]]--
@@ -105,7 +105,7 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/proc/calculate_condorcet_votes(var/blackbox_text)
// https://en.wikipedia.org/wiki/Schulze_method#Implementation
if((mode == "gamemode" || mode == "dynamic") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
if((mode == "gamemode" || mode == "dynamic" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey])
voted -= P.ckey
@@ -155,7 +155,7 @@ SUBSYSTEM_DEF(vote)
for(var/choice in choices)
scores_by_choice += "[choice]"
scores_by_choice["[choice]"] = list()
if((mode == "gamemode" || mode == "dynamic") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
if((mode == "gamemode" || mode == "dynamic" || mode == "roundtype") && CONFIG_GET(flag/must_be_readied_to_vote_gamemode))
for(var/mob/dead/new_player/P in GLOB.player_list)
if(P.ready != PLAYER_READY_TO_PLAY && voted[P.ckey])
voted -= P.ckey
+60
View File
@@ -0,0 +1,60 @@
/datum/atmosphere
var/gas_string
var/id
var/list/base_gases // A list of gases to always have
var/list/normal_gases // A list of allowed gases:base_amount
var/list/restricted_gases // A list of allowed gases like normal_gases but each can only be selected a maximum of one time
var/restricted_chance = 10 // Chance per iteration to take from restricted gases
var/minimum_pressure
var/maximum_pressure
var/minimum_temp
var/maximum_temp
/datum/atmosphere/New()
generate_gas_string()
/datum/atmosphere/proc/generate_gas_string()
var/list/spicy_gas = restricted_gases.Copy()
var/target_pressure = rand(minimum_pressure, maximum_pressure)
var/pressure_scale = target_pressure / maximum_pressure
// First let's set up the gasmix and base gases for this template
// We make the string from a gasmix in this proc because gases need to calculate their pressure
var/datum/gas_mixture/gasmix = new
gasmix.set_temperature(rand(minimum_temp, maximum_temp))
for(var/i in base_gases)
gasmix.set_moles(i, base_gases[i])
// Now let the random choices begin
var/gastype
var/amount
while(gasmix.return_pressure() < target_pressure)
if(!prob(restricted_chance) || !length(spicy_gas))
gastype = pick(normal_gases)
amount = normal_gases[gastype]
else
gastype = pick(spicy_gas)
amount = spicy_gas[gastype]
spicy_gas -= gastype //You can only pick each restricted gas once
amount *= rand(50, 200) / 100 // Randomly modifes the amount from half to double the base for some variety
amount *= pressure_scale // If we pick a really small target pressure we want roughly the same mix but less of it all
amount = CEILING(amount, 0.1)
gasmix.adjust_moles(gastype, amount)
// That last one put us over the limit, remove some of it
if(gasmix.return_pressure() > target_pressure)
var/moles_to_remove = (1 - target_pressure / gasmix.return_pressure()) * gasmix.total_moles()
gasmix.adjust_moles(gastype, -moles_to_remove)
gasmix.set_moles(gastype, FLOOR(gasmix.get_moles(gastype), 0.1))
// Now finally lets make that string
var/list/gas_string_builder = list()
for(var/id in gasmix.get_gases())
gas_string_builder += "[id]=[gasmix.get_moles(id)]"
gas_string_builder += "TEMP=[gasmix.return_temperature()]"
gas_string = gas_string_builder.Join(";")
+48
View File
@@ -0,0 +1,48 @@
// Atmos types used for planetary airs
/datum/atmosphere/lavaland
id = LAVALAND_DEFAULT_ATMOS
base_gases = list(
GAS_O2=5,
GAS_N2=10,
)
normal_gases = list(
GAS_O2=10,
GAS_N2=10,
GAS_CO2=10,
)
restricted_gases = list(
GAS_BZ=0.1,
GAS_METHYL_BROMIDE=0.1,
)
restricted_chance = 30
minimum_pressure = HAZARD_LOW_PRESSURE + 10
maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1
minimum_temp = BODYTEMP_COLD_DAMAGE_LIMIT + 1
maximum_temp = 320
/datum/atmosphere/icemoon
id = ICEMOON_DEFAULT_ATMOS
base_gases = list(
GAS_O2=5,
GAS_N2=10,
)
normal_gases = list(
GAS_O2=10,
GAS_N2=10,
GAS_CO2=10,
)
restricted_gases = list(
GAS_METHYL_BROMIDE=0.1,
)
restricted_chance = 10
minimum_pressure = HAZARD_LOW_PRESSURE + 10
maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1
minimum_temp = 180
maximum_temp = 180
+1
View File
@@ -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>")
@@ -135,6 +135,8 @@
var/obj/item/I = AM
var/mob/M = parent.loc
I.dropped(M)
I.item_flags &= ~IN_STORAGE
I.remove_outline()
if(new_location)
AM.forceMove(new_location) // exited comsig will handle removal reset.
//We don't want to call this if the item is being destroyed
@@ -180,6 +182,7 @@
I.forceMove(parent.drop_location())
return FALSE
I.on_enter_storage(master)
I.item_flags |= IN_STORAGE
refresh_mob_views()
I.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt
if(M)
@@ -423,6 +423,9 @@
var/atom/A = parent
if(ismob(M)) //all the check for item manipulation are in other places, you can safely open any storages as anything and its not buggy, i checked
A.add_fingerprint(M)
if(istype(A, /obj/item))
var/obj/item/I = A
I.remove_outline() //Removes the outline when we drag
if(!over_object)
return FALSE
if(ismecha(M.loc)) // stops inventory actions in a mech
+3 -2
View File
@@ -10,6 +10,7 @@
var/mob/living/L = parent
if(L.incapacitated() || L.lying)
return
var/matrix/otransform = matrix(L.transform) //make a copy of the current transform
animate(L, pixel_z = 4, time = 0)
animate(pixel_z = 0, transform = turn(matrix(), pick(-12, 0, 12)), time=2)
animate(pixel_z = 0, transform = matrix(), time = 0)
animate(pixel_z = 0, transform = turn(L.transform, pick(-12, 0, 12)), time=2) //waddle.
animate(pixel_z = 0, transform = otransform, time = 0) //return to previous transform.
+5 -1
View File
@@ -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
+8
View File
@@ -386,6 +386,14 @@
qdel(language_holder)
var/species_holder = initial(mrace.species_language_holder)
language_holder = new species_holder(src)
var/mob/living/carbon/human/H = src
//provide the user's additional language to the new language holder even if they change species
if(H.additional_language && H.additional_language != "None")
var/language_entry = GLOB.roundstart_languages[H.additional_language]
if(language_entry)
grant_language(language_entry, TRUE, TRUE)
update_atom_languages()
/mob/living/carbon/human/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
@@ -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
View File
@@ -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
+17
View File
@@ -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>")
+12
View File
@@ -329,3 +329,15 @@
to_chat(L, "<span class='warning'>You need an attachable assembly!</span>")
#undef MAXIMUM_EMP_WIRES
//gremlins
/datum/wires/proc/npc_tamper(mob/living/L)
if(!wires.len)
return
var/wire_to_screw = pick(wires)
if(is_color_cut(wire_to_screw) || prob(50)) //CutWireColour() proc handles both cutting and mending wires. If the wire is already cut, always mend it back. Otherwise, 50% to cut it and 50% to pulse it
cut(wire_to_screw)
else
pulse(wire_to_screw, L)
+3 -3
View File
@@ -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()
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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)
@@ -191,6 +191,7 @@
var/x2 = x1
work_squares(y2, x2) //Work squares while in this loop so there's less load
reset_board = FALSE
CHECK_TICK
web += "<table>" //Start setting up the html table
web += "<tbody>"
@@ -235,6 +236,7 @@
web += "<td>[MINESWEEPERIMG(7)]</td>"
if(19)
web += "<td>[MINESWEEPERIMG(8)]</td>"
CHECK_TICK
web += "</tr>"
web += "</table>"
web += "</tbody>"
+18 -9
View File
@@ -1,7 +1,7 @@
/obj/machinery/recharger
name = "recharger"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "recharger0"
icon_state = "recharger"
base_icon_state = "recharger"
desc = "A charging dock for energy based weaponry."
use_power = IDLE_POWER_USE
@@ -12,6 +12,8 @@
var/obj/item/charging = null
var/recharge_coeff = 1
var/using_power = FALSE //Did we put power into "charging" last process()?
///Did we finish recharging the currently inserted item?
var/finished_recharging = FALSE
var/static/list/allowed_devices = typecacheof(list(
/obj/item/gun/energy,
@@ -48,13 +50,14 @@
charging = new_charging
if (new_charging)
START_PROCESSING(SSmachines, src)
finished_recharging = FALSE
use_power = ACTIVE_POWER_USE
using_power = TRUE
update_icon()
update_appearance()
else
use_power = IDLE_POWER_USE
using_power = FALSE
update_icon()
update_appearance()
/obj/machinery/recharger/Exited(atom/movable/M, atom/newloc)
. = ..()
@@ -100,7 +103,8 @@
return 1
if(anchored && !charging)
if(default_deconstruction_screwdriver(user, "rechargeropen", "recharger0", G))
if(default_deconstruction_screwdriver(user, "recharger", "recharger", G))
update_appearance()
return
if(panel_open && G.tool_behaviour == TOOL_CROWBAR)
@@ -133,7 +137,7 @@
C.give(C.chargerate * recharge_coeff)
use_power(250 * recharge_coeff)
using_power = TRUE
update_icon()
update_appearance()
if(istype(charging, /obj/item/ammo_box/magazine/recharge))
var/obj/item/ammo_box/magazine/recharge/R = charging
@@ -141,7 +145,7 @@
R.stored_ammo += new R.ammo_type(R)
use_power(200 * recharge_coeff)
using_power = TRUE
update_icon()
update_appearance()
return
if(istype(charging, /obj/item/ammo_casing/mws_batt))
@@ -152,7 +156,7 @@
using_power = 1
if(R.BB == null)
R.chargeshot()
update_icon(using_power)
update_appearance()
if(istype(charging, /obj/item/ammo_box/magazine/mws_mag))
var/obj/item/ammo_box/magazine/mws_mag/R = charging
@@ -164,14 +168,19 @@
using_power = 1
if(batt.BB == null)
batt.chargeshot()
update_icon(using_power)
update_appearance()
if(!using_power && !finished_recharging) //Inserted thing is at max charge/ammo, notify those around us
finished_recharging = TRUE
playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
say("[charging] has finished recharging!")
else
return PROCESS_KILL
/obj/machinery/recharger/power_change()
..()
update_icon()
update_appearance()
/obj/machinery/recharger/emp_act(severity)
. = ..()
+51 -3
View File
@@ -146,6 +146,9 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
var/list/grind_results //A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only
var/list/juice_results //A reagent list containing blah blah... but when JUICED in a grinder!
//the outline filter on hover
var/outline_filter
/* Our block parry data. Should be set in init, or something if you are using it.
* This won't be accessed without ITEM_CAN_BLOCK or ITEM_CAN_PARRY so do not set it unless you have to to save memory.
* If you decide it's a good idea to leave this unset while turning the flags on, you will runtime. Enjoy.
@@ -175,6 +178,12 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(force_string)
item_flags |= FORCE_STRING_OVERRIDE
if(istype(loc, /obj/item/storage))
item_flags |= IN_STORAGE
if(istype(loc, /obj/item/robot_module))
item_flags |= IN_INVENTORY
if(!hitsound)
if(damtype == "fire")
hitsound = 'sound/items/welder.ogg'
@@ -373,6 +382,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
return
remove_outline()
pickup(user)
add_fingerprint(user)
if(!user.put_in_active_hand(src, FALSE, FALSE))
@@ -443,6 +453,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
qdel(src)
item_flags &= ~IN_INVENTORY
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
remove_outline()
// if(!silent)
// playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
user?.update_equipment_speed_mods()
@@ -557,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)
@@ -867,18 +884,49 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
openToolTip(user,src,params,title = name,content = "[desc]<br><b>Force:</b> [force_string]",theme = "")
/obj/item/MouseEntered(location, control, params)
if((item_flags & IN_INVENTORY) && usr.client.prefs.enable_tips && !QDELETED(src))
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))
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.
var/mob/living/L = usr
if(istype(L) && L.incapacitated())
apply_outline(COLOR_RED_GRAY)
else
apply_outline()
/obj/item/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
. = ..()
remove_outline()
/obj/item/MouseExited(location,control,params)
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXIT, location, control, params)
deltimer(tip_timer)//delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
remove_outline()
/obj/item/MouseEntered(location,control,params)
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params)
/obj/item/proc/apply_outline(colour = null)
if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr))
return
if(usr.client)
if(!usr.client.prefs.outline_enabled)
return
if(!colour)
if(usr.client)
colour = usr.client.prefs.outline_color
if(!colour)
colour = COLOR_BLUE_GRAY
else
colour = COLOR_BLUE_GRAY
if(outline_filter)
filters -= outline_filter
outline_filter = filter(type="outline", size=1, color=colour)
filters += outline_filter
/obj/item/proc/remove_outline()
if(outline_filter)
filters -= outline_filter
outline_filter = null
// Called when a mob tries to use the item as a tool.
// Handles most checks.
+40
View File
@@ -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()
+1 -1
View File
@@ -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
+109 -25
View File
@@ -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,
+1
View File
@@ -3,6 +3,7 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
new/datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("scooter frame", /obj/item/scooter_frame, 10, time = 25, one_per_turf = 0), \
new/datum/stack_recipe("railing", /obj/structure/railing, 3, time = 18, window_checks = TRUE), \
new/datum/stack_recipe("catwalk tile", /obj/item/stack/tile/catwalk, 1, 4, 20), \
))
/obj/item/stack/rods
@@ -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), \
@@ -555,3 +555,10 @@
color = "#92661A"
turf_type = /turf/open/floor/bronze
custom_materials = list(/datum/material/bronze = 250)
/obj/item/stack/tile/catwalk
name = "catwalk tile"
singular_name = "catwalk floor tile"
desc = "Flooring that shows its contents underneath. Engineers love it!"
icon_state = "catwalk_tile"
turf_type = /turf/open/floor/plating/catwalk_floor
+55 -31
View File
@@ -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)
@@ -0,0 +1,44 @@
/**
* ## catwalk flooring
*
* They show what's underneath their catwalk flooring (pipes and the like)
* you can crowbar it to interact with the underneath stuff without destroying the tile...
* unless you want to!
*/
/turf/open/floor/plating/catwalk_floor
icon = 'icons/turf/floors/catwalk_plating.dmi'
icon_state = "catwalk_below"
floor_tile = /obj/item/stack/tile/catwalk
name = "catwalk floor"
desc = "Flooring that shows its contents underneath. Engineers love it!"
baseturfs = /turf/open/floor/plating
footstep = FOOTSTEP_CATWALK
barefootstep = FOOTSTEP_CATWALK
clawfootstep = FOOTSTEP_CATWALK
heavyfootstep = FOOTSTEP_CATWALK
var/covered = TRUE
/turf/open/floor/plating/catwalk_floor/Initialize()
. = ..()
layer = CATWALK_LAYER
update_icon(UPDATE_OVERLAYS)
/turf/open/floor/plating/catwalk_floor/update_overlays()
. = ..()
var/static/catwalk_overlay
if(isnull(catwalk_overlay))
catwalk_overlay = iconstate2appearance(icon, "catwalk_above")
if(covered)
. += catwalk_overlay
/turf/open/floor/plating/catwalk_floor/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
covered = !covered
to_chat(user, span_notice("[!covered ? "You removed the cover!" : "You added the cover!"]"))
update_icon(UPDATE_OVERLAYS)
/turf/open/floor/plating/catwalk_floor/pry_tile(obj/item/crowbar, mob/user, silent)
if(covered)
to_chat(user, span_notice("You need to remove the cover first!"))
return FALSE
. = ..()
+1 -1
View File
@@ -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)
+3
View File
@@ -75,6 +75,9 @@
/obj/machinery/vr_sleeper/MouseDrop_T(mob/target, mob/user)
if(user.lying || !iscarbon(target) || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
return
if(occupant)
to_chat(user, "<span class='boldnotice'>The VR Sleeper is already occupied!</span>")
return
close_machine(target)
ui_interact(user)
+1 -1
View File
@@ -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
+13 -2
View File
@@ -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)
@@ -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
@@ -129,13 +129,12 @@
/mob/living/simple_animal/slaughter/proc/release_victims()
if(!consumed_mobs)
return
var/turf/T = get_turf(src)
if(!T)
T = find_safe_turf()
for(var/mob/living/M in consumed_mobs)
if(!M)
continue
var/turf/T = find_safe_turf()
if(!T)
T = get_turf(src)
M.forceMove(T)
/mob/living/simple_animal/slaughter/proc/refresh_consumed_buff()
@@ -263,12 +262,12 @@
if(!consumed_mobs)
return
var/turf/T = get_turf(src)
if(!T)
T = find_safe_turf()
for(var/mob/living/M in consumed_mobs)
if(!M)
continue
var/turf/T = find_safe_turf()
if(!T)
T = get_turf(src)
continue
M.forceMove(T)
if(M.revive(full_heal = TRUE, admin_revive = TRUE))
M.grab_ghost(force = TRUE)
@@ -45,5 +45,5 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T)
// Any effects that need to be cleaned up if traitor class is being swapped.
/datum/traitor_class/proc/on_process(/datum/antagonist/traitor/T)
/datum/traitor_class/proc/on_process(datum/antagonist/traitor/T)
// only for processing traitor classes; runs once an SSprocessing tick
+10 -8
View File
@@ -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"
@@ -157,6 +157,8 @@
id = GAS_METHANE
specific_heat = 30
name = "Methane"
powerloss_inhibition = 1
heat_resistance = 3
breath_results = GAS_METHYL_BROMIDE
fire_products = list(GAS_CO2 = 1, GAS_H2O = 2)
fire_burn_rate = 0.5
@@ -177,6 +179,8 @@
id = GAS_METHYL_BROMIDE
specific_heat = 42
name = "Methyl Bromide"
powermix = 1
heat_penalty = -1
flags = GAS_FLAG_DANGEROUS
breath_alert_info = list(
not_enough_alert = list(
@@ -32,6 +32,11 @@
if(!blocks_air)
air = new(2500,src)
air.copy_from_turf(src)
if(planetary_atmos && !(initial_gas_mix in SSair.planetary))
var/datum/gas_mixture/mix = new
mix.parse_gas_string(initial_gas_mix)
mix.mark_immutable()
SSair.planetary[initial_gas_mix] = mix
update_air_ref(planetary_atmos ? 1 : 2)
. = ..()
@@ -261,6 +261,8 @@ we use a hook instead
return 1
/datum/gas_mixture/parse_gas_string(gas_string)
gas_string = SSair.preprocess_gas_string(gas_string)
var/list/gas = params2list(gas_string)
if(gas["TEMP"])
var/temp = text2num(gas["TEMP"])
@@ -648,7 +648,7 @@
//Replace miasma with oxygen
var/cleaned_air = min(air.get_moles(GAS_MIASMA), 20 + (air.return_temperature() - FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 70) / 20)
air.adjust_moles(GAS_MIASMA, -cleaned_air)
air.adjust_moles(GAS_O2, cleaned_air)
air.adjust_moles(GAS_METHANE, cleaned_air)
//Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable
air.set_temperature(air.return_temperature() + cleaned_air * 0.002)
@@ -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)
+6
View File
@@ -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"
+38
View File
@@ -60,6 +60,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
//autocorrected this round, not that you'd need to check that.
var/UI_style = null
var/outline_enabled = TRUE
var/outline_color = COLOR_BLUE_GRAY
var/buttons_locked = FALSE
var/hotkeys = FALSE
@@ -117,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
@@ -503,6 +507,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<a href='?_src_=prefs;preference=previous_facehair_style;task=input'>&lt;</a> <a href='?_src_=prefs;preference=next_facehair_style;task=input'>&gt;</a><BR>"
dat += "<span style='border: 1px solid #161616; background-color: #[facial_hair_color];'>&nbsp;&nbsp;&nbsp;</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'>&lt;</a> <a href='?_src_=prefs;preference=next_grad_style;task=input'>&gt;</a><BR>"
dat += "<span style='border: 1px solid #161616; background-color: #[grad_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=grad_color;task=input'>Change</a><BR>"
dat += "</td>"
//Mutant stuff
var/mutant_category = 0
@@ -774,6 +784,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
dat += "<h2>General Settings</h2>"
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
dat += "<b>Outline:</b> <a href='?_src_=prefs;preference=outline_enabled'>[outline_enabled ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>Outline Color:</b> <span style='border:1px solid #161616; background-color: [outline_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=outline_color'>Change</a><BR>"
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
dat += "<b>Show Runechat Chat Bubbles:</b> <a href='?_src_=prefs;preference=chat_on_map'>[chat_on_map ? "Enabled" : "Disabled"]</a><br>"
@@ -1707,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)
@@ -2706,6 +2735,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
buttons_locked = !buttons_locked
if("tgui_fancy")
tgui_fancy = !tgui_fancy
if("outline_enabled")
outline_enabled = !outline_enabled
if("outline_color")
var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null
if(pickedOutlineColor)
outline_color = pickedOutlineColor
if("tgui_lock")
tgui_lock = !tgui_lock
if("winflash")
@@ -3006,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
@@ -3070,6 +3107,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(additional_language && additional_language != "None")
var/language_entry = GLOB.roundstart_languages[additional_language]
if(language_entry)
character.additional_language = language_entry
character.grant_language(language_entry, TRUE, TRUE)
//limb stuff, only done when initially spawning in
@@ -45,6 +45,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(current_version < 46) //If you remove this, remove force_reset_keybindings() too.
force_reset_keybindings_direct(TRUE)
addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
if(current_version < 30)
outline_enabled = TRUE
outline_color = COLOR_BLUE_GRAY
/datum/preferences/proc/update_character(current_version, savefile/S)
if(current_version < 19)
@@ -377,6 +380,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["ooccolor"] >> ooccolor
S["lastchangelog"] >> lastchangelog
S["UI_style"] >> UI_style
S["outline_color"] >> outline_color
S["outline_enabled"] >> outline_enabled
S["hotkeys"] >> hotkeys
S["chat_on_map"] >> chat_on_map
S["max_chat_length"] >> max_chat_length
@@ -555,6 +560,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["ooccolor"], ooccolor)
WRITE_FILE(S["lastchangelog"], lastchangelog)
WRITE_FILE(S["UI_style"], UI_style)
WRITE_FILE(S["outline_enabled"], outline_enabled)
WRITE_FILE(S["outline_color"], outline_color)
WRITE_FILE(S["hotkeys"], hotkeys)
WRITE_FILE(S["chat_on_map"], chat_on_map)
WRITE_FILE(S["max_chat_length"], max_chat_length)
@@ -676,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
@@ -868,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)
@@ -1037,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)
+2
View File
@@ -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)
+17 -17
View File
@@ -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,
+1 -1
View File
@@ -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
+36 -3
View File
@@ -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
+3
View File
@@ -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
+18 -4
View File
@@ -27,13 +27,27 @@
if(prob(low_threat_perc))
severity = "low; the supermatter should return to normal operation shortly."
else
severity = "medium; the supermatter should return to normal operation, but check NT CIMS to ensure this."
severity = "medium; the supermatter should return to normal operation, but regardless, check if the emitters may need to be turned off temporarily."
else
severity = "high; if the supermatter's cooling is not fortified, coolant may need to be added."
severity = "high; the emitters likely need to be turned off, and if the supermatter's cooling loop is not fortified, pre-cooled gas may need to be added."
if(100000 to INFINITY)
severity = "extreme; emergency action is likely to be required even if coolant loop is fine."
severity = "extreme; emergency action is likely to be required even if coolant loop is fine. Turn off the emitters and make sure the loop is properly cooling gases."
if(power > 20000 || prob(round(power/200)))
priority_announce("Supermatter surge detected. Estimated severity is [severity]", "Anomaly Alert")
/datum/round_event/supermatter_surge/start()
GLOB.main_supermatter_engine.matter_power += power
var/obj/machinery/power/supermatter_crystal/supermatter = GLOB.main_supermatter_engine
var/power_proportion = supermatter.powerloss_inhibitor/2 // what % of the power goes into matter power, at most 50%
// we reduce the proportion that goes into actual matter power based on powerloss inhibitor
// primarily so the supermatter doesn't tesla the instant these happen
supermatter.matter_power += power * power_proportion
var/datum/gas_mixture/methane_puff = new
var/selected_gas = pick(4;GAS_CO2, 10;GAS_METHANE, 4;GAS_H2O, 1;GAS_BZ, 1;GAS_METHYL_BROMIDE)
methane_puff.set_moles(selected_gas, 500)
methane_puff.set_temperature(500)
var/energy_ratio = (power * 500 * (1-power_proportion)) / methane_puff.thermal_energy()
if(energy_ratio < 1) // energy output we want is lower than current energy, reduce the amount of gas we puff out
methane_puff.set_moles(GAS_METHANE, energy_ratio * 500)
else // energy output we want is higher than current energy, increase its actual heat
methane_puff.set_temperature(energy_ratio * 500)
supermatter.assume_air(methane_puff)
+12
View File
@@ -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

Some files were not shown because too many files have changed in this diff Show More