From b42a27c7126f7ba002be76936ebfa5e123d6f0f1 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Tue, 18 Apr 2017 04:55:14 -0500 Subject: [PATCH 01/52] tgstation-server 2.0 --- tools/tgstation-server/Fix Errors.bat | 24 ++++ .../Merge pr without updating.bat | 112 ++++++++++++++++ tools/tgstation-server/OnServerBoot.bat | 47 +++++++ tools/tgstation-server/README.md | 111 ++++++++++++++++ tools/tgstation-server/README.txt | 56 -------- tools/tgstation-server/Recompile.bat | 51 ++++++++ .../tgstation-server/Reset and Recompile.bat | 66 ++++++++++ tools/tgstation-server/Start Bot.bat | 6 +- tools/tgstation-server/Start Server.bat | 29 ++++- tools/tgstation-server/Update Server.bat | 63 +++++---- tools/tgstation-server/Update to PR.bat | 121 ++++++++++++++++++ .../Update without resetting.bat | 100 +++++++++++++++ tools/tgstation-server/bin/activepr.bat | 1 + tools/tgstation-server/bin/build.bat | 11 +- tools/tgstation-server/bin/copyfromgit.bat | 8 +- tools/tgstation-server/bin/findab.bat | 22 +++- tools/tgstation-server/bin/findbyond.bat | 1 - tools/tgstation-server/bin/findgit.bat | 1 - tools/tgstation-server/bin/getcurdate.bat | 1 - tools/tgstation-server/bin/getunixtime.bat | 14 +- tools/tgstation-server/bin/updategit.bat | 14 +- tools/tgstation-server/config.bat | 36 ++---- tools/tgstation-server/copyexclude.txt | 2 +- tools/tgstation-server/install.bat | 19 +-- 24 files changed, 771 insertions(+), 145 deletions(-) create mode 100644 tools/tgstation-server/Fix Errors.bat create mode 100644 tools/tgstation-server/Merge pr without updating.bat create mode 100644 tools/tgstation-server/OnServerBoot.bat create mode 100644 tools/tgstation-server/README.md delete mode 100644 tools/tgstation-server/README.txt create mode 100644 tools/tgstation-server/Recompile.bat create mode 100644 tools/tgstation-server/Reset and Recompile.bat create mode 100644 tools/tgstation-server/Update to PR.bat create mode 100644 tools/tgstation-server/Update without resetting.bat create mode 100644 tools/tgstation-server/bin/activepr.bat diff --git a/tools/tgstation-server/Fix Errors.bat b/tools/tgstation-server/Fix Errors.bat new file mode 100644 index 0000000000..bbe540c82c --- /dev/null +++ b/tools/tgstation-server/Fix Errors.bat @@ -0,0 +1,24 @@ +@echo off +title Automated Error Fixer. +echo This will reset some things, the byond server (DreamDaemon) and the start server script must not be running. Some error messages are normal. +echo You will be prompted to press any key 3 times +pause +pause +pause +cls +echo Resetting folders +mkdir gamecode\a +mkdir gamecode\b +del /S /F /Q gamefolder >nul 2>nul +rmdir /S /q gamefolder +mklink /d gamefolder gamecode\a +call bin\findab.bat +cls +echo Re-Initializing code +call bin\copyfromgit.bat +cls +echo Recompiling the game. If you plan to update or testmerge you can just close this program now and continue. +call bin\build.bat +cls +echo Done! (hopefully) +pause \ No newline at end of file diff --git a/tools/tgstation-server/Merge pr without updating.bat b/tools/tgstation-server/Merge pr without updating.bat new file mode 100644 index 0000000000..da4e023477 --- /dev/null +++ b/tools/tgstation-server/Merge pr without updating.bat @@ -0,0 +1,112 @@ +@echo off +@title Server Updater +SETLOCAL ENABLEDELAYEDEXPANSION +set HOME = %USERPROFILE% + +call config.bat +rem if the first arg to nudge.py is not a channel, it is treated as the "source" +if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" +call bin\getcurdate.bat +call bin\findgit.bat +echo This will handle merging a pr locally, compiling the server, and applying the PR test job. + +:PROMPT +SET /P UserInput=Please enter the pr number (without a # or anything of the sorts): +SET /A PR=UserInput + +if %PR% EQU %UserInput% ( + if %PR% GTR 0 ( + echo updating to pr %PR% + ) else ( + echo Bad input + goto PROMPT + ) +) else ( + echo Bad input + goto PROMPT +) +if exist updating.lk ( + echo ERROR: A current update script has been detected running. if you know this is a mistake: + pause + echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: + pause +) + +echo lock>updating.lk + +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "PR test job started. Merging PR #%PR% locally" >nul 2>nul + + +cd gitrepo +git fetch origin pull/%PR%/head:pr-%PR% +if %ERRORLEVEL% neq 0 ( + cd .. + echo git fetch failed. Aborting test merge. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting test merge" + del updating.lk >nul 2>nul + pause + exit /b 1 +) +git merge pr-%PR% +if %ERRORLEVEL% neq 0 ( + cd .. + echo git merge of PR #%PR% failed. Aborting test merge + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "git merge of PR #%PR% failed. Aborting PR test merge" >nul 2>nul + cd gitrepo + git merge --abort + if !ERRORLEVEL! neq 0 ( + echo ERROR: Error aborting test merge, resetting repo. + cd .. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Error aborting test merge, resetting git repo to head" >nul 2>nul + cd gitrepo + git reset --hard + git clean -fd + bin/activepr.bat + del /F /Q prtestjob.lk >nul 2>nul + echo NOTICE: We had to reset the repo's state, all other active test merges were undone. + ) + cd .. + del updating.lk >nul 2>nul + pause + exit /b 1 +) +cd .. + +echo %PR%>>prtestjob.lk +> bin/activepr.bat + +echo ################################## +echo ################################## +echo: +echo Test merging pr done, compiling in 10 seconds. If you want to preform other actions (like more test merges) You can close this now and do them. +echo: +del updating.lk >nul 2>nul + +timeout 10 + +echo lock>updating.lk +call bin\findab.bat + +call bin\copyfromgit.bat + +echo compiling change log +cd gamecode\%AB% +call python tools\ss13_genchangelog.py html/changelog.html html/changelogs +cd ..\.. + +echo Compiling game. +call bin\build.bat +if %DM_EXIT% neq 0 ( + echo DM compile failed. Aborting. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed. Aborting test merge." >nul 2>nul + del /F /Q updating.lk >nul 2>nul + pause + exit /b 1 +) + +del updating.lk >nul 2>nul +rmdir /q gamefolder +mklink /d gamefolder gamecode\%AB% >nul +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Test merge job finished. Test merge will take place next round." >nul 2>nul +echo Done. The test merge will automatically take place at round restart. +timeout 300 \ No newline at end of file diff --git a/tools/tgstation-server/OnServerBoot.bat b/tools/tgstation-server/OnServerBoot.bat new file mode 100644 index 0000000000..db1f5cc230 --- /dev/null +++ b/tools/tgstation-server/OnServerBoot.bat @@ -0,0 +1,47 @@ +@echo off +title Server Boot Detected. +echo This script is only meant to start byond when the server first boots, do not manually run this. +echo If you manually ran this, close this window NOW. +timeout 15 +start cmd /c "Start Bot.bat" +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Prepping code +mkdir gamecode\a +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Prepping code +mkdir gamecode\b +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Prepping code +del /S /F /Q gamefolder >nul 2>nul +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Prepping code +rmdir /S /q gamefolder +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Prepping code +mklink /d gamefolder gamecode\a +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Prepping code +call bin\findab.bat +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Re-Initializing code +call bin\copyfromgit.bat +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Re-Initializing code +cls +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Compiling the game. If you plan to update or testmerge you can just close this program now and continue. +call bin\build.bat +cls +echo Server boot detected. Starting byond and Space Station 13. +echo Starting server and bot. +start cmd /c "Start Server.bat" +timeout 10 \ No newline at end of file diff --git a/tools/tgstation-server/README.md b/tools/tgstation-server/README.md new file mode 100644 index 0000000000..10831fd461 --- /dev/null +++ b/tools/tgstation-server/README.md @@ -0,0 +1,111 @@ +# Tgstation Toolkit: +This is a toolset to manage a production server of /tg/Station13 (and its forks). It includes an update script that is able to update the server without having to stop or shutdown the server (the update will take effect next round) and a script to start the server and restart it if it crashes (optional, requires registry tweaks to disable the Windows crash dialog system wide) as well as systems for fixing errors and merging GitHub Pull Requests locally. + +Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on github or a local repo using file:/// urls. + +These tools require UAC to be disabled. (There has been limited luck getting it to work under UAC on windows 10 using run as admin, but in other versions of windows, running as admin resets the current directory and this breaks things) +(Note: There is no security risk to disabling UAC because UAC is not a security boundary, there exists active unpatched exploits against it that have been around since vista and work up to windows 10) + +## Install: +1. Move this folder to where you want your server to run from (you may also rename this folder if you wish) +1. Right click on `config.bat` and select `Edit`. +1. Configure the port, git repo, and other setting, You may configure the location of git, but if you installed using git for windows, we will auto detect it if its not in path. +1. You may also need to change the location of the git repo url, and if you renamed your `dme`/"dream maker environment" file, you will need to change the project name configuration setting to be the name of your dme minus the extension (eg: if you renamed `tgstation.dme` to `ntstation.dme`, project name should be set to `ntstation` +1. Finally, run install.bat and hope for no error. + * It will clone the git repo, setup some folders, and add cross reference links everywhere. + +### Optional: +If you plan to use the `Start Server.bat` script to start the server and restart it if it crashes, you need to run the `disable crash dialog.reg` registry script to disable the "program.exe has stopped working" dialog. This will get disabled system wide so if you rely on this dialog, you should skip this step. + +## Usage: +### Folders: +* `gamecode/` + * This will house two copies of the game code, one for updating and one for live. When updating, it will automatically swap them. + +* `gamedata/` + * This contains the `data/` and `config/` folders from the code. They are stored here and a symbolic link is created in the `gamecode/` folders pointing to here. + * This also makes backing them up easy. +(you may copy and paste your existing `data/` and `config/` folders here after the install script has ran.) + +* `bot/` + * This is a copy of the bot folder. you should run the bot from here.a link to nudge.py is created in the code folders so that the game can use the announcement feature. + * The start server script and update script will send messages out thru the bot, but if its not running or python is not installed, they will gracefully continue what they are doing. + +* `gamefolder/` + * This is a symbolic link pointing to current "live" folder. + * When the server is updated, we just point this to the updating folder so that the update takes place next round. + +* `gitrepo/` + * This contains the actual git repository, all changes in here will be overwritten during update operations, the configured branch will always be forced to track from live. + * On the first update of the day, the current code state is saved to a branch called `backup-YYYY-MM-DD` before updating, to make local reverts easy. + * Following update operations on the same day do not make branches because I was too lazy to detect such edge cases. + +* `bin/` + * This contains random helper batch files. + * Running these on their own is a bad idea. + +### Starting the game server: +To run the game server, Run `Start Server.bat` + +It will restart the game server if it shutdowns for any reason, with delays if the game server had recently been (re)started. + + +### Updating the server: +To update the server, just run `Update Server.bat`. (it will git pull, compile, all that jazz) + +(Note: Updating automatically does a code reset, clearing ALL changes to the local git repo, including test merges (explained below) and manual changes (This will not change any configs/settings or clear any data in the `gamedata/` folder)) + +Updates do not require the server to be shutdown, changes will apply next round if the server is currently running. + +Updates create a branch with the current state of the repo called `backup-YYYY-MM-DD` (only one is created in any given day) + +There is also a `Update without resetting.bat` file that does the same without resetting the code, used to update without clearing test merges or local changes. Prone to merge conflicts. + + +### Locally merge GitHub Pull Requests (PR test merge): +This feature currently only works if github is the remote(git server), it could be adapted for gitlab as well. + +Running these will merge the pull request then recompile the server, changes take effect the next round if the server is currently running. + +There are multiple flavors: +* `Update To PR.bat` + * Updates the server, resetting state, and merges a PR(Pull Request) by number. +* `Merge PR Without Updating.bat` + * Merges a PR without updating the server before hand or resetting the state (can be used to test merge multiple PRs). + +You can clear all active test merges using `Reset and Recompile.bat` (explained below) + +### Resetting, Recompiling, and troubleshooting. +* `Recompile.bat` + * Just recompiles the game code and stages it to apply next round +* `Reset and Recompile.bat` + * Like the above but resets the git repo to the state of the last update operation (clearing any changes or test merges) (Does not reset `gamedata/` data/settings) + * Generally used to clear out test merges +* `Fix Errors.bat` + * Requires the server not be running, rebuilds the staging A/B folders and then does all of the above. (Used to fix errors that can prevent the server from starting or cause it to crash on reboot) + +### Using the bot +1. Install python 3 +1. Edit the config in `bot/` as needed +1. Start the bot using the `Start Bot.bat` file + + +### Starting everything when the computer/server boots +1. Use autologin to set it up so the user account logs in at boot + * (There are some security implications with doing this, this is only a tiny bit more secure then putting a file on the hard drive with the server's remote login password titled `totally not a password.txt`) + * https://technet.microsoft.com/en-us/sysinternals/bb963905 +1. Setup something to run OnServerBoot.bat on login (setting up a link/shortcut to this file in the startup folder of the start menu works, creating a scheduled task in windows administrative tools also works) +1. OnServerBoot.bat does not update, but it does re-compile and reinitialize the A/B folders. + +### Updating byond after this is all setup +1. Download installer from byond website +1. Close watch dog/start server script +1. Wait for current round to end +1. Exit DreamDeamon +1. Run installer +1. After installing, run recompile.bat (or update if you want) +1. Run start server.bat + + + + diff --git a/tools/tgstation-server/README.txt b/tools/tgstation-server/README.txt deleted file mode 100644 index 5f8d0c143a..0000000000 --- a/tools/tgstation-server/README.txt +++ /dev/null @@ -1,56 +0,0 @@ -About: - This is a toolset to manage a production server of tgstation13 (or its forks). It includes an update script that is able to update the server without having to stop or shutdown the server (the update will take effect next round) and a script to start the server and restart it if it crashes (optional, requires registry tweaks to disable the Windows crash dialog system wide) - - This will force a live tracking of the configured git repo, so no local modifications are allowed. (this is to avoid issues with merge conflicts and because I'm too lazy to make the script detect them) - - -Install: - Move this folder to where you want your server to run from (you may also rename this folder if you wish) - Right click on config.bat and click edit. - Configure the port, selected map file, and the like, You may configure the location of git, but if you installed using git for windows, we will auto detect it if its not in path. - You may also need to change the location of the gitrepo url, and if you renamed your dme/"dream maker environment" file, you will need to change the project name configuration setting to be the name of your dme minus the extension (eg: if you renamed tgstation.dme to ntstation.dme, project name should be set to ntstation - - After that, just run install.bat and hope for no error. - - It will clone the git repo, setup some folders, and add cross reference links everywhere. - - Optional: - If you plan to use the start-server script to watch the server and restart it if it crashes, you should run the "disable crash dialog.reg" registry script to disable the "program.exe has stopped working" dialog. This will get disabled system wide so if you rely on this dialog, you shouldn't run the script. - Even without this, byond stopping the world from two many critical errors (like infinite loops and recursions) will still be detected. - -Usage: - The install script will make a few folders: - gamecode - This will house two copies of the game code, one for updating and one for live. When updating, it will automatically swap them. - - gamedata - This contains the data, config, and cfg folders from the code. They are stored here and a symbolic link is created in the code folders pointing to here. - This also makes backing them up easy. - (you may copy and paste your existing data/config/cfg folders here after the install script has ran.) - - bot - This is a copy of the bot folder. you should run the bot from here. a link to coredata.py and nudge.py is created in the code folders so that the game can use the announcement feature. - The start server script and update script will send messages out thru the bot, but if its not running or python is not installed, they will gracefully continue what they are doing. - - gamefolder - This is a symbolic link pointing to current "live" folder. - When the server is updated, we just point this to the updating folder so that the update takes place next round. - - bin - This contains random helper batch files. - Running these on their own is a bad idea. - - To update the server, just run update server.bat. (it will git pull, compile, all that jazz) - It will ask you if you want to apply the update live. 99.9% of the time, this will not cause issues. The only issues it can cause relate to changes to media files(images(but not icons)/css/html/sound not stored in the RSC. but only new clients will see those issues, and at worst, its a minor graphical glitch. - You may remove the pause in the update script at line 90 if you like. - - To run the server, just run start server.bat - It will automatically redirect the runtimes to data/logs/runtimes/runtime-YYYY-MM-DD.log. - (Note: It will not automatically roll them over, but every time it crashes or stops it will have a new log file.) - When it starts dreamdaemon, it instructs byond to close down dreamdaemon if the world stops for any reason, this includes hitting the stop button, dreamdaemon shutting the world down because of too many runtimes/infinite loops. - If dreamdaemon ever stops while the start server script is running, the script will restart it automatically. - (Note: The script can not detect hangs or lockups) - - - - \ No newline at end of file diff --git a/tools/tgstation-server/Recompile.bat b/tools/tgstation-server/Recompile.bat new file mode 100644 index 0000000000..40b746365e --- /dev/null +++ b/tools/tgstation-server/Recompile.bat @@ -0,0 +1,51 @@ +@echo off +@title Server Updater +set HOME = %USERPROFILE% + +call config.bat +call bin\getcurdate.bat + +echo This will handle recompiling the server, and applying the new version. +echo ready? + +timeout 120 + +if exist updating.lk ( + echo ERROR: A current update script has been detected running. if you know this is a mistake: + pause + echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: + pause +) + +echo lock>updating.lk + +rem if the first arg to nudge.py is not a channel, it is treated as the "source" +if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" + +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Recompile job started" >nul 2>nul + +call bin\findab.bat + +call bin\copyfromgit.bat + +echo compiling change log +cd gamecode\%AB% +call python tools\ss13_genchangelog.py html/changelog.html html/changelogs +cd ..\.. + +echo Compiling game. +call bin\build.bat +if %DM_EXIT% neq 0 ( + echo DM compile failed. Aborting. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting recompile." >nul 2>nul + del /F /Q updating.lk >nul 2>nul + pause + exit /b 1 +) + +del updating.lk >nul 2>nul +rmdir /q gamefolder +mklink /d gamefolder gamecode\%AB% >nul +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Recompile finished. Recompiled code will take place next round." >nul 2>nul +echo Done. The recompile will automatically take place at round restart. +timeout 300 \ No newline at end of file diff --git a/tools/tgstation-server/Reset and Recompile.bat b/tools/tgstation-server/Reset and Recompile.bat new file mode 100644 index 0000000000..f0127fd3ac --- /dev/null +++ b/tools/tgstation-server/Reset and Recompile.bat @@ -0,0 +1,66 @@ +@echo off +@title Server Updater +set HOME = %USERPROFILE% + +call config.bat +call bin\getcurdate.bat +call bin\findgit.bat + +echo This will handle resetting the git repo, recompiling the server, and applying the new version. +echo Ready? + +timeout 120 + +if exist updating.lk ( + echo ERROR: A current update script has been detected running. if you know this is a mistake: + pause + echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: + pause +) + +echo lock>updating.lk + +rem if the first arg to nudge.py is not a channel, it is treated as the "source" +if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" + +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Reset local changes job started" >nul 2>nul + +cd gitrepo +git reset --hard +git clean -df +cd .. +echo ################################## +echo ################################## +echo: +echo Resetting done, compiling in 10 seconds. If you want to preform other actions (like test merges) You can close this now and do them. +echo: +del updating.lk >nul 2>nul + +timeout 10 + +echo lock>updating.lk +call bin\findab.bat + +call bin\copyfromgit.bat + +echo compiling change log +cd gamecode\%AB% +call python tools\ss13_genchangelog.py html/changelog.html html/changelogs +cd ..\.. + +echo Compiling game. +call bin\build.bat +if %DM_EXIT% neq 0 ( + echo DM compile failed. Aborting. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting Reset." >nul 2>nul + del /F /Q updating.lk >nul 2>nul + pause + exit /b 1 +) + +del updating.lk >nul 2>nul +rmdir /q gamefolder +mklink /d gamefolder gamecode\%AB% >nul +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Reset finished. Reset will take place next round." >nul 2>nul +echo Done. The Reset will automatically take place at round restart. +timeout 300 diff --git a/tools/tgstation-server/Start Bot.bat b/tools/tgstation-server/Start Bot.bat index c0631dabbf..a3b1cba0d8 100644 --- a/tools/tgstation-server/Start Bot.bat +++ b/tools/tgstation-server/Start Bot.bat @@ -1,7 +1,7 @@ @echo off @title NT IRC BOT -echo Welcome to the start bot script, This will start the bot and make sure it stays running. This assumes python in the path. To continue, press any key or wait 60 seconds. -timeout 60 +echo Welcome to the start bot script, This will start the bot and make sure it stays running. This assumes python in the path. To continue, press any key or wait 15 seconds. +timeout 15 cd bot :START call ..\bin\getcurdate.bat @@ -9,7 +9,7 @@ if not exist ..\gamedata\data\logs\bot mkdir ..\gamedata\data\logs\bot\ cls echo NT IRC Bot echo Bot Running. Watching for Bot exits. -start /WAIT python NanoTrasenBot.py >>..\gamedata\data\logs\bot\bot-%CUR_DATE%.txt +start /WAIT python minibot.py ^>^>..\gamedata\data\logs\bot\bot-%CUR_DATE%.txt cls echo NT IRC Bot echo Bot exit detected. Restarting in 15 minutes. diff --git a/tools/tgstation-server/Start Server.bat b/tools/tgstation-server/Start Server.bat index 26d3008b34..80efb9444b 100644 --- a/tools/tgstation-server/Start Server.bat +++ b/tools/tgstation-server/Start Server.bat @@ -3,22 +3,37 @@ call config.bat call bin\findbyond.bat -echo Welcome to the start server watch dog script, This will start the server and make sure it stays running. To continue, press any key or wait 60 seconds. -timeout 60 +echo Welcome to the start server watch dog script, This will start the server and make sure it stays running. To continue, press any key or wait 30 seconds. +timeout 30 if not exist gamedata\data\logs\runtimes mkdir gamedata\data\logs\runtimes\ -@python bot\nudge.py "WATCHDOG" "Watch Dog online. Starting server" >nul 2>nul +@call python bot\nudge.py "WATCHDOG" "Watch Dog online. Starting server" >nul 2>nul :START + call bin\getcurdate.bat + +call bin\getunixtime.bat UNIXTIME + +echo %UNIXTIME% + +set STARTTIME=%UNIXTIME% + cls echo Watch Dog. echo Server Running. Watching for server exits. -start /WAIT /ABOVENORMAL "" dreamdaemon.exe gamefolder\%PROJECTNAME%.dmb -port %PORT% -trusted -public -close -log "data\logs\runtimes\runtime-%CUR_DATE%.log" +start /WAIT /ABOVENORMAL "" dreamdaemon.exe gamefolder\%PROJECTNAME%.dmb -port %PORT% -trusted -close -public -verbose cls + +call bin\getunixtime.bat UNIXTIME + +SET /A Result=%UNIXTIME% - %STARTTIME% +SET /A Result=180 - (%Result%/3) +if %Result% LSS 0 set /A Result=0 + echo Watch Dog. -echo Server exit detected. Restarting in 60 seconds. -@python bot\nudge.py "WATCHDOG" "Server exit detected. Restarting server in 60 seconds." >nul 2>nul -timeout 60 +echo Server exit detected. Restarting in %Result% seconds. +@python bot\nudge.py "WATCHDOG" "Server exit detected. Restarting server in %Result% seconds." >nul 2>nul +timeout %Result% goto :START diff --git a/tools/tgstation-server/Update Server.bat b/tools/tgstation-server/Update Server.bat index f7e48f5fe8..1d7d8644af 100644 --- a/tools/tgstation-server/Update Server.bat +++ b/tools/tgstation-server/Update Server.bat @@ -1,53 +1,79 @@ @echo off -@title Server Updater +title Server Updater +SETLOCAL ENABLEDELAYEDEXPANSION set HOME = %USERPROFILE% + call config.bat call bin\getcurdate.bat + echo This will handle downloading git, compiling the server, and applying the update. -echo ready? +echo Ready? + timeout 120 + if exist updating.lk ( - echo ERROR! A current update script has been detected running. if you know this is a mistake: + echo ERROR: A current update script has been detected running. if you know this is a mistake: pause echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: pause ) + +if exist prtestjob.lk ( + call bin/activepr.bat + echo WARNING: The server is currently testing the following PRs !PR!. This update would override that. Do you still want to update? Close this window if not, otherwise: + pause +) + +del /F /Q prtestjob.lk >nul 2>nul + echo lock>updating.lk + bin/activepr.bat rem if the first arg to nudge.py is not a channel, it is treated as the "source" if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" -python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job started" >nul 2>nul +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job started" >nul 2>nul call bin\updategit.bat if %GIT_EXIT% neq 0 ( echo git pull failed. Aborting update - python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git pull failed. Aborting update" - @del updating.lk >nul 2>nul + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting update" + del updating.lk >nul 2>nul pause exit /b 1 ) + if defined PUSHCHANGELOGTOGIT ( + cd gitrepo echo compiling change log python tools\ss13_genchangelog.py html/changelog.html html/changelogs - if %ERRORLEVEL% == 0 ( + if !ERRORLEVEL! == 0 ( echo pushing compiled changelog to server git add -u html/changelog.html git add -u html/changelogs git commit -m "Automatic changelog compile, [ci skip]" - if %ERRORLEVEL% == 0 ( + if !ERRORLEVEL! == 0 ( git push ) REM an error here generally means there was nothing to commit. ) + cd .. ) +echo ################################## +echo ################################## +echo: +echo Updating done, compiling in 10 seconds. If you want to preform other actions (like test merge) You can close this now and do them. +echo: +del updating.lk >nul 2>nul + +timeout 10 + +echo lock>updating.lk call bin\findab.bat call bin\copyfromgit.bat - - if not defined PUSHCHANGELOGTOGIT ( echo compiling change log cd gamecode\%AB% @@ -55,26 +81,19 @@ if not defined PUSHCHANGELOGTOGIT ( cd ..\.. ) - - -echo building script. +echo Compiling game. call bin\build.bat if %DM_EXIT% neq 0 ( echo DM compile failed. Aborting. - python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting update." >nul 2>nul - @del /F /Q updating.lk >nul 2>nul + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting update." >nul 2>nul + del /F /Q updating.lk >nul 2>nul pause exit /b 1 ) -if not defined NOWAITUPDATES ( - echo OK, compiled and ready. So at the hit of a button, we can apply the update live. Technically speaking, it's best to wait until near round end, but unless a html/css/js file in the code had been deleted, edited, or moved recently, no ill effects of applying the update will happen, and the worst is that new clients have display oddities relating to in game windows. Existing connections should have no issue. - echo Ready? - pause -) -@del updating.lk >nul 2>nul +del updating.lk >nul 2>nul rmdir /q gamefolder mklink /d gamefolder gamecode\%AB% >nul -python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job finished. Update will take place next round." >nul 2>nul +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job finished. Update will take place next round." >nul 2>nul echo Done. The update will automatically take place at round restart. timeout 300 \ No newline at end of file diff --git a/tools/tgstation-server/Update to PR.bat b/tools/tgstation-server/Update to PR.bat new file mode 100644 index 0000000000..b6c57b98b7 --- /dev/null +++ b/tools/tgstation-server/Update to PR.bat @@ -0,0 +1,121 @@ +@echo off +title Server Updater +SETLOCAL ENABLEDELAYEDEXPANSION +set HOME = %USERPROFILE% + +call config.bat +rem if the first arg to nudge.py is not a channel, it is treated as the "source" +if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" +call bin\getcurdate.bat +call bin\findgit.bat +echo This will handle Resetting the repo downloading from git, merging the pr locally, compiling the server, and applying the PR test job. + +:PROMPT +SET /P UserInput=Please enter the pr number (without a # or anything of the sorts): +SET /A PR=UserInput + +if %PR% EQU %UserInput% ( + if %PR% GTR 0 ( + echo updating to pr %PR% + ) else ( + echo Bad input + goto PROMPT + ) +) else ( + echo Bad input + goto PROMPT +) +if exist updating.lk ( + echo ERROR: A current update script has been detected running. if you know this is a mistake: + pause + echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: + pause +) + +echo lock>updating.lk + bin/activepr.bat +del /F /Q prtestjob.lk >nul 2>nul + +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "PR test job started. Updating master and merging PR #%PR% locally" >nul 2>nul + +call bin\updategit.bat +if %GIT_EXIT% neq 0 ( + echo git pull failed. Aborting update + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting update" + del updating.lk >nul 2>nul + pause + exit /b 1 +) + +if defined PUSHCHANGELOGTOGIT ( + cd gitrepo + echo compiling change log + call python tools\ss13_genchangelog.py html/changelog.html html/changelogs + if !ERRORLEVEL! == 0 ( + echo pushing compiled changelog to server + git add -u html/changelog.html + git add -u html/changelogs + git commit -m "Automatic changelog compile, [ci skip]" + if !ERRORLEVEL! == 0 ( + git push + ) + REM an error here generally means there was nothing to commit. + ) + cd .. +) + +cd gitrepo +git fetch origin pull/%PR%/head:pr-%PR% +git merge pr-%PR% +if %ERRORLEVEL% neq 0 ( + git reset --hard + git clean -fd + cd .. + echo git merge of PR #%PR% failed. Aborting test merge. + echo An update was successfully preformed but not applied, if you want you can apply it by running recompile.bat + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "git merge of PR #%PR% failed. Aborting PR test job" >nul 2>nul + del updating.lk >nul 2>nul + pause + exit /b 1 +) +cd .. + +echo %PR%>prtestjob.lk +> bin/activepr.bat + +echo ################################## +echo ################################## +echo: +echo Updating to pr done, compiling in 10 seconds. If you want to preform other actions (like more test merges) You can close this now and do them. +echo: +del updating.lk >nul 2>nul + +timeout 10 + +echo lock>updating.lk +call bin\findab.bat + +call bin\copyfromgit.bat + + +echo compiling change log (again) +cd gamecode\%AB% +call python tools\ss13_genchangelog.py html/changelog.html html/changelogs +cd ..\.. + +echo Compiling game. +call bin\build.bat +if %DM_EXIT% neq 0 ( + echo DM compile failed. Aborting. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed. Aborting test merge." >nul 2>nul + del /F /Q updating.lk >nul 2>nul + pause + exit /b 1 +) + +del updating.lk >nul 2>nul +rmdir /q gamefolder +mklink /d gamefolder gamecode\%AB% >nul +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Test merge job finished. Test merge will take place next round." >nul 2>nul +echo Done. The test merge will automatically take place at round restart. +timeout 300 \ No newline at end of file diff --git a/tools/tgstation-server/Update without resetting.bat b/tools/tgstation-server/Update without resetting.bat new file mode 100644 index 0000000000..9a08573fb1 --- /dev/null +++ b/tools/tgstation-server/Update without resetting.bat @@ -0,0 +1,100 @@ +@echo off +@title Server Updater +SETLOCAL ENABLEDELAYEDEXPANSION +set HOME = %USERPROFILE% + +call config.bat +call bin\getcurdate.bat +call bin\findgit.bat + +echo This will update the server without resetting local changes like test merges. +echo Note: This doesn't update the changelog like a normal update does. +echo Ready? + +timeout 120 + +if exist updating.lk ( + echo ERROR: A current update script has been detected running. if you know this is a mistake: + pause + echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: + pause +) + +echo lock>updating.lk + +rem if the first arg to nudge.py is not a channel, it is treated as the "source" +if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" + +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job started (No reset mode)" >nul 2>nul + +cd gitrepo +git fetch origin +if %ERRORLEVEL% neq 0 ( + cd .. + echo git fetch failed. Aborting update. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting update" + del updating.lk >nul 2>nul + pause + exit /b 1 +) +git merge origin/%REPO_BRANCH% +if %ERRORLEVEL% neq 0 ( + cd .. + echo git merge of upstream master failed, aborting update. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "git merge of upstream master failed, aborting update." >nul 2>nul + cd gitrepo + git merge --abort + if %ERRORLEVEL% neq 0 ( + echo ERROR: Error aborting update, resetting repo. + cd .. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Error aborting merge, Resetting git repo" >nul 2>nul + cd gitrepo + git reset --hard + git clean -fd + bin/activepr.bat + del /F /Q prtestjob.lk >nul 2>nul + echo NOTICE: We had to reset the repo's state, all active test merges were undone. + ) + cd .. + del updating.lk >nul 2>nul + pause + exit /b 1 +) +cd .. + + +echo ################################## +echo ################################## +echo: +echo In place update done, compiling in 10 seconds. If you want to preform other actions (like more test merges) You can close this now and do them. +echo: +del updating.lk >nul 2>nul + +timeout 10 + +echo lock>updating.lk +call bin\findab.bat + +call bin\copyfromgit.bat + +echo compiling change log +cd gamecode\%AB% +call python tools\ss13_genchangelog.py html/changelog.html html/changelogs +cd ..\.. + +echo Compiling game. +call bin\build.bat +if %DM_EXIT% neq 0 ( + echo DM compile failed. Aborting. + call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting update." >nul 2>nul + del /F /Q updating.lk >nul 2>nul + pause + exit /b 1 +) + +del updating.lk >nul 2>nul +rmdir /q gamefolder +mklink /d gamefolder gamecode\%AB% >nul +call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job finished. Update will take place next round." >nul 2>nul +echo Done. The update will automatically take place at round restart. +timeout 300 diff --git a/tools/tgstation-server/bin/activepr.bat b/tools/tgstation-server/bin/activepr.bat new file mode 100644 index 0000000000..554142925d --- /dev/null +++ b/tools/tgstation-server/bin/activepr.bat @@ -0,0 +1 @@ +set PR= \ No newline at end of file diff --git a/tools/tgstation-server/bin/build.bat b/tools/tgstation-server/bin/build.bat index 6b9163fe86..049154ce92 100644 --- a/tools/tgstation-server/bin/build.bat +++ b/tools/tgstation-server/bin/build.bat @@ -1,4 +1,3 @@ -@echo off call config.bat call bin/findbyond.bat set DME_FOLDER=gamefolder\ @@ -7,11 +6,19 @@ if defined AB set DME_FOLDER=gamecode\%AB%\ set DME_LOCATION=%DME_FOLDER%%PROJECTNAME%.dme set MDME_LOCATION=%DME_FOLDER%%PROJECTNAME%.mdme +@del %MDME_LOCATION% >nul 2>nul +if defined MAPROTATE set MAPFILE=%MAPROTATE% +if not defined MAPFILE goto BUILD + +echo #define MAP_OVERRIDE >>%MDME_LOCATION% +echo #include "_maps\%MAPFILE%.dm" >>%MDME_LOCATION% + :BUILD echo #define SERVERTOOLS 1 >>%MDME_LOCATION% +echo #define PUTONHUB 1 >>%MDME_LOCATION% type %DME_LOCATION% >>%MDME_LOCATION% -dm %MDME_LOCATION% +dm -clean %MDME_LOCATION% set DM_EXIT=%ERRORLEVEL% @del %DME_FOLDER%%PROJECTNAME%.dmb >nul 2>nul @del %DME_FOLDER%%PROJECTNAME%.rsc >nul 2>nul diff --git a/tools/tgstation-server/bin/copyfromgit.bat b/tools/tgstation-server/bin/copyfromgit.bat index 9f25dcad98..4de01db772 100644 --- a/tools/tgstation-server/bin/copyfromgit.bat +++ b/tools/tgstation-server/bin/copyfromgit.bat @@ -2,11 +2,10 @@ echo Removing old files rem delete the symlinks manually to ensure their targets don't get recursively deleted rmdir /q gamecode\%AB%\data >nul 2>nul rmdir /q gamecode\%AB%\config >nul 2>nul -rmdir /q gamecode\%AB%\cfg >nul 2>nul del /q gamecode\%AB%\nudge.py >nul 2>nul -del /q gamecode\%AB%\CORE_DATA.py >nul 2>nul +del /q gamecode\%AB%\libmysql.dll >nul 2>nul -del /S /F /Q gamecode\%AB% >nul 2>nul +del /S /F /Q gamecode\%AB%\ >nul 2>nul echo Copying files xcopy gitrepo gamecode\%AB% /Y /X /K /R /H /I /C /V /E /Q /EXCLUDE:copyexclude.txt >nul @@ -14,9 +13,8 @@ mkdir gamecode\%AB%\.git\logs copy gitrepo\.git\logs\HEAD gamecode\%AB%\.git\logs\HEAD /D /V /Y >nul mklink gamecode\%AB%\nudge.py ..\..\bot\nudge.py >nul -mklink gamecode\%AB%\CORE_DATA.py ..\..\bot\CORE_DATA.py >nul rmdir /q gamecode\%AB%\data >nul 2>nul rmdir /s /q gamecode\%AB%\data >nul 2>nul mklink /d gamecode\%AB%\data ..\..\gamedata\data >nul mklink /d gamecode\%AB%\config ..\..\gamedata\config >nul -mklink /d gamecode\%AB%\cfg ..\..\gamedata\cfg >nul \ No newline at end of file +mklink gamecode\%AB%\libmysql.dll ..\..\gamedata\libmysql.dll >nul diff --git a/tools/tgstation-server/bin/findab.bat b/tools/tgstation-server/bin/findab.bat index 9bb94bed14..0c24a7571b 100644 --- a/tools/tgstation-server/bin/findab.bat +++ b/tools/tgstation-server/bin/findab.bat @@ -1,5 +1,5 @@ -@del gamecode\a\updater.temp >nul 2>nul -@del gamecode\b\updater.temp >nul 2>nul +del gamecode\a\updater.temp >nul 2>nul +del gamecode\b\updater.temp >nul 2>nul echo test >gamefolder\updater.temp @@ -15,6 +15,14 @@ if exist gamecode\a\updater.temp ( set AB=b ) else ( echo Current folder detected to be the "A" folder. Game is not currently running, Updating to the "A" folder. + if exist gamecode\b\%PROJECTNAME%.rsc.lk ( + rem we attempt to delete the lock file to see if the server is currently running. + del /q gamecode\b\%PROJECTNAME%.rsc.lk >nul 2>nul + if exist gamecode\b\%PROJECTNAME%.rsc.lk set RUNNING=1 + rmdir /q gamefolder + mklink /d gamefolder gamecode\b >nul + echo Game is in actually currently running on the "B" folder, Resetting current folder to the "B" folder first + ) set AB=a ) ) else if exist gamecode\b\updater.temp ( @@ -23,7 +31,15 @@ if exist gamecode\a\updater.temp ( set AB=a ) else ( echo Current folder detected to be the "B" folder. Game is not currently running, Updating to the "B" folder. + if exist gamecode\a\%PROJECTNAME%.rsc.lk ( + rem we attempt to delete the lock file to see if the server is currently running. + del /q gamecode\a\%PROJECTNAME%.rsc.lk >nul 2>nul + if exist gamecode\a\%PROJECTNAME%.rsc.lk set RUNNING=1 + rmdir /q gamefolder + mklink /d gamefolder gamecode\a >nul + echo Game is in actually currently running on the "A" folder, Resetting current folder to the "A" folder first + ) set AB=b ) ) -@del gamefolder\updater.temp >nul 2>nul \ No newline at end of file +del gamefolder\updater.temp >nul 2>nul \ No newline at end of file diff --git a/tools/tgstation-server/bin/findbyond.bat b/tools/tgstation-server/bin/findbyond.bat index 2e015e4e62..ff07af2dd6 100644 --- a/tools/tgstation-server/bin/findbyond.bat +++ b/tools/tgstation-server/bin/findbyond.bat @@ -1,4 +1,3 @@ -@echo off @dm.exe -h >nul 2>nul IF %ERRORLEVEL% NEQ 9009 ( diff --git a/tools/tgstation-server/bin/findgit.bat b/tools/tgstation-server/bin/findgit.bat index 059dfd6626..9c802b54c6 100644 --- a/tools/tgstation-server/bin/findgit.bat +++ b/tools/tgstation-server/bin/findgit.bat @@ -1,4 +1,3 @@ -@echo off REM check if git is already in path git --version >nul 2>nul && goto :eof diff --git a/tools/tgstation-server/bin/getcurdate.bat b/tools/tgstation-server/bin/getcurdate.bat index 84c19f7dd5..f5360756ce 100644 --- a/tools/tgstation-server/bin/getcurdate.bat +++ b/tools/tgstation-server/bin/getcurdate.bat @@ -1,3 +1,2 @@ -@echo off FOR /f %%a in ('WMIC OS GET LocalDateTime ^| find "."') DO set DTS=%%a set CUR_DATE=%DTS:~0,4%-%DTS:~4,2%-%DTS:~6,2% \ No newline at end of file diff --git a/tools/tgstation-server/bin/getunixtime.bat b/tools/tgstation-server/bin/getunixtime.bat index 64672391e8..2b4115faab 100644 --- a/tools/tgstation-server/bin/getunixtime.bat +++ b/tools/tgstation-server/bin/getunixtime.bat @@ -1,2 +1,12 @@ -@echo off -for /f "delims=" %%x in ('cscript /nologo unixtime.vbs') do set UNIXTIME=%%x \ No newline at end of file +REM setlocal +call :GetUnixTime UNIXTIME +goto :EOF + +:GetUnixTime +REM setlocal enableextensions +for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do ( + set %%x) +set /a z=(14-100%Month%%%100)/12, y=10000%Year%%%10000-z +set /a ut=y*365+y/4-y/100+y/400+(153*(100%Month%%%100+12*z-3)+2)/5+Day-719469 +set /a ut=ut*86400+100%Hour%%%100*3600+100%Minute%%%100*60+100%Second%%%100 +endlocal & set "%1=%ut%" & goto :EOF \ No newline at end of file diff --git a/tools/tgstation-server/bin/updategit.bat b/tools/tgstation-server/bin/updategit.bat index 024e17dfed..e16cfc3922 100644 --- a/tools/tgstation-server/bin/updategit.bat +++ b/tools/tgstation-server/bin/updategit.bat @@ -1,18 +1,12 @@ call bin\findgit.bat echo Updating repo + cd gitrepo + git branch backup-%CUR_DATE% >nul 2>nul git fetch set GIT_EXIT=%ERRORLEVEL% -if %GIT_EXIT% neq 0 goto END -git checkout %REPO_BRANCH% -set GIT_EXIT=%ERRORLEVEL% -if %GIT_EXIT% neq 0 goto END -git reset origin/%REPO_BRANCH% --hard -set GIT_EXIT=%ERRORLEVEL% -if %GIT_EXIT% neq 0 goto END -git pull --force -set GIT_EXIT=%ERRORLEVEL% -:END +git reset origin/%REPO_BRANCH% --hard + cd .. \ No newline at end of file diff --git a/tools/tgstation-server/config.bat b/tools/tgstation-server/config.bat index 201c17abbe..eb3ecd13e7 100644 --- a/tools/tgstation-server/config.bat +++ b/tools/tgstation-server/config.bat @@ -1,50 +1,42 @@ @echo off -REM Server Tools configuration file. Lines starting with REM are comments and ignored. -REM on/off config options are considered "on" if they have anything (even 0) and "off" if they are blank or commented out. +REM Server Tools configuration file. Lines starting with rem are comments and ignored. REM This must be set to the name of your dme without the .dme part. (should be fine leaving this alone unless you renamed the code) set PROJECTNAME=tgstation -REM location of the repo. +REM location of the repo. (use an ssh url if you plan to push compiled changlogs) +REM Only set during install, do not re-run install.bat if you change this, instead manually change the remote of the gitrepo folder using git tools set REPO_URL=https://github.com/tgstation/-tg-station.git -REM set REPO_URL=git@github.com:tgstation/-tg-station.git REM What branch of the repo to use. set REPO_BRANCH=master -REM what map file to use. This should be the name of the dm, not dmm (and without the .dm part) (defaults to what ever is ticked in the dme) -set MAPFILE=tgstation2 +REM Override Map (This disables map roation and forces the selected map to always be loaded) +REM set MAPFILE=tgstation2 REM set MAPFILE=metastation -REM set MAPFILE=ministation -REM port to use (only used to start the server in the start-server script) -set PORT=1337 +REM port to use when starting the server +set PORT=2337 -REM This is the channel to log updates to. Leave blank to log to the normal channel (this is done via the tgstation bot, optional) -set UPDATE_LOG_CHANNEL=#coderbus - - -REM overrides the prompt to live apply the updates in update server.bat if set to anything other than a null string. -REM It is generally safe to live apply the updates, they don't take effect until the next round. the only concern is that some media files may get loaded by new clients before the next round. These files aren't edited 99% of the time, so its not a real concern, but I kept the prompt the default for compatibility sake. -set NOWAITUPDATES= +REM This is the channel to log updates to. Leave blank to log to the bot's default logging channel (this is done via the tgstation minibot bot, optional) +set UPDATE_LOG_CHANNEL=#devbus,#coderbus,#tgstation13 REM Attempt to push the compiled changelog to the configured git server? (set to anything) -REM This requires you configure git with authentication for the upstream server. (the ssh key should be stored in c:\users\USERNAME_HERE\.ssh\ as the filename id_rsa (if that still doesn't work, try c:\program files\git\.ssh\id_rsa)) +REM This requires you configure git with authentication for the upstream server. (git for windows users should just put an ssh key in c:\users\USERNAME_HERE\.ssh\ as the filename id_rsa) And you should have installed this with a git, ssh, or file url) set PUSHCHANGELOGTOGIT= REM location of git. The script will attempt to auto detect this, but if it fails, you can set it manually. -REM This will be added to the end of path as is (only for the batch file, not the whole system) REM github for windows users see http://www.chambaud.com/2013/07/08/adding-git-to-path-when-using-github-for-windows/ (an example is provided below) + set GIT_LOCATION_PATH= -REM set GIT_LOCATION_PATH=C:\Users\\AppData\Local\GitHub\PortableGit_\bin;C:\Users\\AppData\Local\GitHub\PortableGit_\cmd +REM set GIT_LOCATION_PATH=C:\Users\Administrator\AppData\Local\GitHub\PortableGit_c2ba306e536fdf878271f7fe636a147ff37326ad\bin;C:\Users\Administrator\AppData\Local\GitHub\PortableGit_c2ba306e536fdf878271f7fe636a147ff37326ad\cmd - -REM path to the byond bin folder. (if blank, we look in path, program files/byond/bin, and program files (x86)/byond/bin) (same rules as git path above) -set BYOND_LOCATION_PATH= \ No newline at end of file +REM path to the byond bin folder. (same rules as git path above, almost always auto detected, but you could point this to the output of the zip version for install-less setups, and even abuse that to make updating byond versions less of a pain by just changing this config and then crashing the server.) +set BYOND_LOCATION_PATH= diff --git a/tools/tgstation-server/copyexclude.txt b/tools/tgstation-server/copyexclude.txt index a790724e14..3cf4643303 100644 --- a/tools/tgstation-server/copyexclude.txt +++ b/tools/tgstation-server/copyexclude.txt @@ -4,6 +4,6 @@ gitrepo\config\ gitrepo\data\ gitrepo\bot\ -gitrepo\cfg\ gitrepo\.git\ +gitrepo\libmysql.dll diff --git a/tools/tgstation-server/install.bat b/tools/tgstation-server/install.bat index e6089b0497..f37908966a 100644 --- a/tools/tgstation-server/install.bat +++ b/tools/tgstation-server/install.bat @@ -29,7 +29,6 @@ echo Repo downloaded. echo Setting up folders... mkdir gamecode\a mkdir gamecode\b -mkdir gamecode\override mkdir gamedata mkdir bot @@ -37,8 +36,8 @@ echo Copying things around.... echo (1/3) xcopy gitrepo\data gamedata\data /Y /X /K /R /H /I /C /V /E /Q >nul xcopy gitrepo\config gamedata\config /Y /X /K /R /H /I /C /V /E /Q >nul -xcopy gitrepo\cfg gamedata\cfg /Y /X /K /R /H /I /C /V /E /Q >nul xcopy gitrepo\bot bot /Y /X /K /R /H /I /C /V /E /Q >nul +copy gitrepo\libmysql.dll gamedata\libmysql.dll /D /V /Y >nul echo (2/3) xcopy gitrepo gamecode\a /Y /X /K /R /H /I /C /V /E /Q /EXCLUDE:copyexclude.txt >nul mkdir gamecode\a\.git\logs\ @@ -51,25 +50,27 @@ echo done. echo Setting up symbolic links. mklink gamecode\a\nudge.py ..\..\bot\nudge.py -mklink gamecode\a\CORE_DATA.py ..\..\bot\CORE_DATA.py +mklink gamecode\a\libmysql.dll ..\..\gamedata\libmysql.dll mklink /d gamecode\a\data ..\..\gamedata\data mklink /d gamecode\a\config ..\..\gamedata\config -mklink /d gamecode\a\cfg ..\..\gamedata\cfg mklink gamecode\b\nudge.py ..\..\bot\nudge.py -mklink gamecode\b\CORE_DATA.py ..\..\bot\CORE_DATA.py +mklink gamecode\b\libmysql.dll ..\..\gamedata\libmysql.dll mklink /d gamecode\b\data ..\..\gamedata\data mklink /d gamecode\b\config ..\..\gamedata\config -mklink /d gamecode\b\cfg ..\..\gamedata\cfg mklink /d gamefolder gamecode\a +echo prepping python (if installed) +pip install PyYaml +pip install beautifulsoup4 + echo Compiling for the first time. echo Compiling change log. -cd gamecode\a +cd gamefolder call python tools\ss13_genchangelog.py html/changelog.html html/changelogs -cd ..\.. +cd .. echo Compiling game. call bin\build.bat if %DM_EXIT% neq 0 echo DM compile failed. @@ -77,4 +78,4 @@ if %DM_EXIT% neq 0 echo DM compile failed. echo Done. You may start the server using the start server program or change the game config in gamedata\config pause -:ABORT \ No newline at end of file +:ABORT From d5b0150e99e3a9eea57842ef7b912de49299f2bb Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:36:38 -0500 Subject: [PATCH 02/52] Buffs hierophant club --- .../modules/mining/lavaland/necropolis_chests.dm | 16 +++++++++++----- .../hostile/megafauna/hierophant.dm | 10 +++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 4999f796c5..9e4996135d 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -1,5 +1,6 @@ //The chests dropped by mob spawner tendrils. Also contains associated loot. + /obj/structure/closet/crate/necropolis name = "necropolis chest" desc = "It's watching you closely." @@ -820,7 +821,9 @@ timer = world.time + cooldown_time if(isliving(target) && chaser_timer <= world.time) //living and chasers off cooldown? fire one! chaser_timer = world.time + chaser_cooldown - new /obj/effect/overlay/temp/hierophant/chaser(get_turf(user), user, target, chaser_speed, friendly_fire_check) + var/obj/effect/overlay/temp/hierophant/chaser/C = new(get_turf(user), user, target, chaser_speed, friendly_fire_check) + C.damage = 30 + C.monster_damage_boost = FALSE add_logs(user, target, "fired a chaser at", src) else INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) //otherwise, just do cardinal blast @@ -940,10 +943,10 @@ new /obj/effect/overlay/temp/hierophant/telegraph/teleport(T, user) new /obj/effect/overlay/temp/hierophant/telegraph/teleport(source, user) for(var/t in RANGE_TURFS(1, T)) - var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, user, TRUE) //blasts produced will not hurt allies + var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, user, TRUE) //No friendly fire, this is a utility tool B.damage = 30 for(var/t in RANGE_TURFS(1, source)) - var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, user, TRUE) //but absolutely will hurt enemies + var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, user, TRUE) B.damage = 30 for(var/mob/living/L in range(1, source)) INVOKE_ASYNC(src, .proc/teleport_mob, source, L, T, user) //regardless, take all mobs near us along @@ -1004,7 +1007,9 @@ for(var/i in 1 to range) if(!J) return - new /obj/effect/overlay/temp/hierophant/blast(J, user, friendly_fire_check) + var/obj/effect/overlay/temp/hierophant/blast/B = new(J, user, friendly_fire_check) + B.damage = 30 + B.monster_damage_boost = FALSE previousturf = J J = get_step(previousturf, dir) @@ -1015,4 +1020,5 @@ playsound(T,'sound/effects/bin_close.ogg', 200, 1) sleep(2) for(var/t in RANGE_TURFS(1, T)) - new /obj/effect/overlay/temp/hierophant/blast(t, user, friendly_fire_check) + var/obj/effect/overlay/temp/hierophant/blast/B = new(t, user, friendly_fire_check) + B.damage = 15 //keeps monster damage boost due to lower damage diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 03f8ce63cf..79ac234ab9 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -478,6 +478,8 @@ Difficulty: Hard var/speed = 3 //how many deciseconds between each step var/currently_seeking = FALSE var/friendly_fire_check = FALSE //if blasts produced apply friendly fire + var/monster_damage_boost = TRUE + var/damage = 10 /obj/effect/overlay/temp/hierophant/chaser/Initialize(mapload, new_caster, new_target, new_speed, is_friendly_fire) . = ..() @@ -521,9 +523,10 @@ Difficulty: Hard moving-- sleep(speed) targetturf = get_turf(target) - /obj/effect/overlay/temp/hierophant/chaser/proc/make_blast() - new /obj/effect/overlay/temp/hierophant/blast(loc, caster, friendly_fire_check) + var/obj/effect/overlay/temp/hierophant/blast/B = new(loc, caster, friendly_fire_check) + B.damage = damage + B.monster_damage_boost = monster_damage_boost /obj/effect/overlay/temp/hierophant/telegraph icon = 'icons/effects/96x96.dmi' @@ -553,6 +556,7 @@ Difficulty: Hard desc = "Get out of the way!" duration = 9 var/damage = 10 //how much damage do we do? + var/monster_damage_boost = TRUE //do we deal extra damage to monsters? Used by the boss var/list/hit_things = list() //we hit these already, ignore them var/friendly_fire_check = FALSE var/bursting = FALSE //if we're bursting and need to hit anyone crossing us @@ -595,7 +599,7 @@ Difficulty: Hard var/limb_to_hit = L.get_bodypart(pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg")) var/armor = L.run_armor_check(limb_to_hit, "melee", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 50, "Your armor was penetrated by [src]!") L.apply_damage(damage, BURN, limb_to_hit, armor) - if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) + if(monster_damage_boost && (ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid))) L.adjustBruteLoss(damage) add_logs(caster, L, "struck with a [name]") for(var/obj/mecha/M in T.contents - hit_things) //and mechs. From 5956761df82bf8cf04ee555e4c993f2844d2aaff Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:36:43 -0500 Subject: [PATCH 03/52] Adds a new toy to arcade prizes. A rumbling toy toolbox meant to mimic hisgrace --- code/game/machinery/computer/arcade.dm | 3 ++- code/game/objects/items/toys.dm | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 643282ab4a..1fdfd61f07 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -43,7 +43,8 @@ /obj/item/weapon/grenade/chem_grenade/glitter/pink = 1, /obj/item/weapon/grenade/chem_grenade/glitter/blue = 1, /obj/item/weapon/grenade/chem_grenade/glitter/white = 1, - /obj/item/toy/eightball = 2) + /obj/item/toy/eightball = 2, + /obj/item/toy/windupToolbox = 2) light_color = LIGHT_COLOR_GREEN diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 5353341ab6..69e00e057e 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -279,6 +279,28 @@ resistance_flags = FLAMMABLE +/obj/item/toy/windupToolbox + name = "windup toolbox" + desc = "A replica toolbox that rumbles when you turn the key" + icon_state = "his_grace" + item_state = "artistic_toolbox" + var/active = FALSE + icon = 'icons/obj/weapons.dmi' + attack_verb = list("robusted") + +/obj/item/toy/windupToolbox/attack_self(mob/user) + if(!active) + icon_state = "his_grace_awakened" + to_chat(user, "You wind up [src], it begins to rumble.") + active = TRUE + addtimer(CALLBACK(src, .proc/stopRumble), 600) + else + to_chat(user, "[src] is already active.") + +/obj/item/toy/windupToolbox/proc/stopRumble() + icon_state = initial(icon_state) + active = FALSE + /* * Subtype of Double-Bladed Energy Swords */ From 4cab22661128b2502ba7ac69f00d9e3af36e2a78 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:36:46 -0500 Subject: [PATCH 04/52] Plasmaman Plasma Tanks are just as large as normal emergency tanks --- code/game/objects/items/weapons/tanks/tank_types.dm | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 code/game/objects/items/weapons/tanks/tank_types.dm diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm old mode 100644 new mode 100755 index f1f16a69b7..4ffa4a9b89 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -140,6 +140,8 @@ item_state = "plasmaman_tank_belt" slot_flags = SLOT_BELT force = 5 + volume = 3 + w_class = WEIGHT_CLASS_SMALL //thanks i forgot this /obj/item/weapon/tank/internals/plasmaman/belt/full/New() ..() From 36072177f9000d8290cc4be2dc38b708b227a45f Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:36:50 -0500 Subject: [PATCH 05/52] [READY]Peacekeeper cyborg projectile dampening fields, attempt three --- code/__DEFINES/misc.dm | 4 + code/controllers/subsystem/fields.dm | 27 ++ code/game/objects/items/robot/robot_items.dm | 107 ++++++ code/game/turfs/space/space.dm | 6 +- code/game/turfs/turf.dm | 37 ++- code/modules/fields/fields.dm | 309 ++++++++++++++++++ code/modules/fields/peaceborg_dampener.dm | 106 ++++++ code/modules/fields/turf_objects.dm | 76 +++++ .../mob/living/silicon/robot/robot_modules.dm | 3 +- icons/effects/fields.dmi | Bin 0 -> 1939 bytes tgstation.dme | 4 + 11 files changed, 657 insertions(+), 22 deletions(-) create mode 100644 code/controllers/subsystem/fields.dm create mode 100644 code/modules/fields/fields.dm create mode 100644 code/modules/fields/peaceborg_dampener.dm create mode 100644 code/modules/fields/turf_objects.dm create mode 100644 icons/effects/fields.dmi diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 321bbf7b67..8b02a61fba 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -407,3 +407,7 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE //Error handler defines #define ERROR_USEFUL_LEN 2 + +#define NO_FIELD 0 +#define FIELD_TURF 1 +#define FIELD_EDGE 2 diff --git a/code/controllers/subsystem/fields.dm b/code/controllers/subsystem/fields.dm new file mode 100644 index 0000000000..8e9f387478 --- /dev/null +++ b/code/controllers/subsystem/fields.dm @@ -0,0 +1,27 @@ + +SUBSYSTEM_DEF(fields) + name = "Fields" + wait = 2 + priority = 40 + flags = SS_KEEP_TIMING + var/list/datum/proximity_monitor/advanced/running = list() + var/list/datum/proximity_monitor/advanced/currentrun = list() + +/datum/controller/subsystem/fields/fire(resumed = 0) + if(!resumed) + src.currentrun = running.Copy() + var/list/currentrun = src.currentrun + while(currentrun.len) + var/datum/proximity_monitor/advanced/F = currentrun[currentrun.len] + currentrun.len-- + if(!F.requires_processing) + continue + F.process() + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/fields/proc/register_new_field(datum/proximity_monitor/advanced/F) + running += F + +/datum/controller/subsystem/fields/proc/unregister_field(datum/proximity_monitor/advanced/F) + running -= F diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index f38cad82f2..abf7f5b827 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -504,6 +504,113 @@ S.change_head_color(color2) dropped = TRUE +//Peacekeeper Cyborg Projectile Dampenening Field +/obj/item/borg/projectile_dampen + name = "Hyperkinetic Dampening projector" + desc = "A device that projects a dampening field that weakens kinetic energy above a certain threshold. Projects a field that drains power per second \ + while active, that will weaken and slow damaging projectiles inside its field. Still being a prototype, it tends to induce a charge on ungrounded metallic surfaces." + icon = 'icons/obj/device.dmi' + icon_state = "shield" + var/maxenergy = 1500 + var/energy = 1500 + var/energy_recharge = 7.5 + var/energy_recharge_cyborg_drain_coefficient = 0.4 + var/cyborg_cell_critical_percentage = 0.05 + var/mob/living/silicon/robot/host = null + var/datum/proximity_monitor/advanced/dampening_field + var/projectile_damage_coefficient = 0.5 + var/projectile_damage_tick_ecost_coefficient = 2 //Lasers get half their damage chopped off, drains 50 power/tick. Note that fields are processed 5 times per second. + var/projectile_speed_coefficient = 1.5 //Higher the coefficient slower the projectile. + var/projectile_tick_speed_ecost = 15 + var/current_damage_dampening = 0 + var/list/obj/item/projectile/tracked + var/image/projectile_effect + var/field_radius = 3 + +/obj/item/borg/projectile_dampen/debug + maxenergy = 50000 + energy = 50000 + energy_recharge = 5000 + +/obj/item/borg/projectile_dampen/Initialize() + . = ..() + projectile_effect = image('icons/effects/fields.dmi', "projectile_dampen_effect") + tracked = list() + icon_state = "shield0" + START_PROCESSING(SSfastprocess, src) + +/obj/item/borg/projectile_dampen/Destroy() + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/item/borg/projectile_dampen/attack_self(mob/user) + var/active = FALSE + if(!istype(dampening_field)) + activate_field() + active = TRUE + else + deactivate_field() + active = FALSE + to_chat(user, "You [active? "activate":"deactivate"] the [src].") + icon_state = "[initial(icon_state)][active]" + +/obj/item/borg/projectile_dampen/proc/activate_field() + if(!istype(dampening_field)) + dampening_field = make_field(/datum/proximity_monitor/advanced/peaceborg_dampener, list("current_range" = field_radius, "host" = src, "projector" = src)) + +/obj/item/borg/projectile_dampen/proc/deactivate_field() + QDEL_NULL(dampening_field) + visible_message("The [src] shuts off!") + for(var/obj/item/projectile/P in tracked) + restore_projectile(P) + +/obj/item/borg/projectile_dampen/process() + process_recharge() + process_usage() + update_location() + +/obj/item/borg/projectile_dampen/proc/update_location() + if(dampening_field) + dampening_field.HandleMove() + +/obj/item/borg/projectile_dampen/proc/process_usage() + var/usage = 0 + for(var/I in tracked) + if(!tracked[I]) //No damage + continue + usage += projectile_tick_speed_ecost + usage += (current_damage_dampening * projectile_damage_tick_ecost_coefficient) + energy = Clamp(energy - usage, 0, maxenergy) + if(energy <= 0) + deactivate_field() + visible_message("The [src] blinks \"ENERGY DEPLETED\"") + +/obj/item/borg/projectile_dampen/proc/process_recharge() + if(!istype(host)) + energy = Clamp(energy + energy_recharge, 0, maxenergy) + return + if((host.cell.charge >= (host.cell.maxcharge * cyborg_cell_critical_percentage)) && (energy < maxenergy)) + host.cell.use(energy_recharge*energy_recharge_cyborg_drain_coefficient) + energy += energy_recharge + +/obj/item/borg/projectile_dampen/proc/dampen_projectile(obj/item/projectile/P, track_projectile = TRUE) + if(tracked[P]) + return + if(track_projectile) + tracked[P] = P.damage + current_damage_dampening += P.damage + P.damage *= projectile_damage_coefficient + P.speed *= projectile_speed_coefficient + P.add_overlay(projectile_effect) + +/obj/item/borg/projectile_dampen/proc/restore_projectile(obj/item/projectile/P) + tracked -= P + P.damage *= (1/projectile_damage_coefficient) + P.speed *= (1/projectile_speed_coefficient) + P.cut_overlay(projectile_effect) + current_damage_dampening -= P.damage + + /********************************************************************** HUD/SIGHT things ***********************************************************************/ diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index a0940eded1..1876d6f782 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -42,7 +42,7 @@ if (opacity) has_opaque_atom = TRUE - + return INITIALIZE_HINT_NORMAL /turf/open/space/attack_ghost(mob/dead/observer/user) @@ -177,7 +177,7 @@ ChangeTurf(/turf/open/floor/plating) return TRUE return FALSE - + /turf/open/space/ReplaceWithLattice() var/dest_x = destination_x var/dest_y = destination_y @@ -186,4 +186,4 @@ destination_x = dest_x destination_y = dest_y destination_z = dest_z - + diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 96e3b9c590..c4d35d7bd1 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -92,39 +92,39 @@ LC.attackby(C,user) return coil.place_turf(src, user) - return 1 + return TRUE - return 0 + return FALSE /turf/CanPass(atom/movable/mover, turf/target, height=1.5) - if(!target) return 0 + if(!target) return FALSE if(istype(mover)) // turf/Enter(...) will perform more advanced checks return !density else // Now, doing more detailed checks for air movement and air group formation if(target.blocks_air||blocks_air) - return 0 + return FALSE for(var/obj/obstacle in src) if(!obstacle.CanPass(mover, target, height)) - return 0 + return FALSE for(var/obj/obstacle in target) if(!obstacle.CanPass(mover, src, height)) - return 0 + return FALSE - return 1 + return TRUE /turf/Enter(atom/movable/mover as mob|obj, atom/forget as mob|obj|turf|area) if (!mover) - return 1 + return TRUE // First, make sure it can leave its square if(isturf(mover.loc)) // Nothing but border objects stop you from leaving a tile, only one loop is needed for(var/obj/obstacle in mover.loc) if(!obstacle.CheckExit(mover, src) && obstacle != mover && obstacle != forget) mover.Bump(obstacle, 1) - return 0 + return FALSE var/list/large_dense = list() //Next, check objects to block entry that are on the border @@ -132,14 +132,14 @@ if(border_obstacle.flags & ON_BORDER) if(!border_obstacle.CanPass(mover, mover.loc, 1) && (forget != border_obstacle)) mover.Bump(border_obstacle, 1) - return 0 + return FALSE else large_dense += border_obstacle //Then, check the turf itself if (!src.CanPass(mover, src)) mover.Bump(src, 1) - return 0 + return FALSE //Finally, check objects/mobs to block entry that are not on the border var/atom/movable/tompost_bump @@ -151,8 +151,9 @@ top_layer = obstacle.layer if(tompost_bump) mover.Bump(tompost_bump,1) - return 0 - return 1 //Nothing found to block so return success! + return FALSE + + return TRUE //Nothing found to block so return success! /turf/Entered(atom/movable/AM) if(explosion_level && AM.ex_check(explosion_id)) @@ -185,7 +186,7 @@ O.make_unfrozen() /turf/proc/is_plasteel_floor() - return 0 + return FALSE /turf/proc/levelupdate() for(var/obj/O in src) @@ -303,7 +304,7 @@ if(src_object.contents.len) to_chat(usr, "You start dumping out the contents...") if(!do_after(usr,20,target=src_object)) - return 0 + return FALSE var/list/things = src_object.contents.Copy() var/datum/progressbar/progress = new(user, things.len, src) @@ -311,7 +312,7 @@ sleep(1) qdel(progress) - return 1 + return TRUE ////////////////////////////// //Distance procs @@ -325,7 +326,7 @@ // possible. It results in more efficient (CPU-wise) pathing // for bots and anything else that only moves in cardinal dirs. /turf/proc/Distance_cardinal(turf/T) - if(!src || !T) return 0 + if(!src || !T) return FALSE return abs(x - T.x) + abs(y - T.y) //////////////////////////////////////////////////// @@ -341,7 +342,7 @@ return(2) /turf/proc/can_have_cabling() - return 1 + return TRUE /turf/proc/can_lay_cable() return can_have_cabling() & !intact diff --git a/code/modules/fields/fields.dm b/code/modules/fields/fields.dm new file mode 100644 index 0000000000..4b083d7e02 --- /dev/null +++ b/code/modules/fields/fields.dm @@ -0,0 +1,309 @@ + +//Movable and easily code-modified fields! Allows for custom AOE effects that affect movement and anything inside of them, and can do custom turf effects! +//Supports automatic recalculation/reset on movement. +//If there's any way to make this less CPU intensive than I've managed, gimme a call or do it yourself! - kevinz000 + +//Field shapes +#define FIELD_NO_SHAPE 0 //Does not update turfs automatically +#define FIELD_SHAPE_RADIUS_SQUARE 1 //Uses current_range and square_depth_up/down +#define FIELD_SHAPE_CUSTOM_SQUARE 2 //Uses square_height and square_width and square_depth_up/down + +//Proc to make fields. make_field(field_type, field_params_in_associative_list) +/proc/make_field(field_type, list/field_params, override_checks = FALSE, start_field = TRUE) + var/datum/proximity_monitor/advanced/F = new field_type() + if(!F.assume_params(field_params) && !override_checks) + QDEL_NULL(F) + if(!F.check_variables() && !override_checks) + QDEL_NULL(F) + if(start_field && (F || override_checks)) + F.Initialize() + return F + +/datum/proximity_monitor/advanced + var/name = "\improper Energy Field" + //Field setup specifications + var/field_shape = FIELD_NO_SHAPE + var/square_height = 0 + var/square_width = 0 + var/square_depth_up = 0 + var/square_depth_down = 0 + //Processing + var/requires_processing = FALSE + var/process_inner_turfs = FALSE //Don't do this unless it's absolutely necessary + var/process_edge_turfs = FALSE //Don't do this either unless it's absolutely necessary, you can just track what things are inside manually or on the initial setup. + var/setup_edge_turfs = FALSE //Setup edge turfs/all field turfs. Set either or both to ON when you need it, it's defaulting to off unless you do to save CPU. + var/setup_field_turfs = FALSE + + var/list/turf/field_turfs = list() + var/list/turf/edge_turfs = list() + var/list/turf/field_turfs_new = list() + var/list/turf/edge_turfs_new = list() + +/datum/proximity_monitor/advanced/New() + SSfields.register_new_field(src) + +/datum/proximity_monitor/advanced/Destroy() + SSfields.unregister_field(src) + full_cleanup() + return ..() + +/datum/proximity_monitor/advanced/proc/assume_params(list/field_params) + var/pass_check = TRUE + for(var/param in field_params) + if(vars[param] || isnull(vars[param]) || (param in vars)) + vars[param] = field_params[param] + else + pass_check = FALSE + return pass_check + +/datum/proximity_monitor/advanced/proc/check_variables() + var/pass = TRUE + if(field_shape == FIELD_NO_SHAPE) //If you're going to make a manually updated field you shouldn't be using automatic checks so don't. + pass = FALSE + if(current_range < 0 || square_height < 0 || square_width < 0 || square_depth_up < 0 || square_depth_down < 0) + pass = FALSE + if(!istype(host)) + pass = FALSE + return pass + +/datum/proximity_monitor/advanced/process() + if(process_inner_turfs) + for(var/turf/T in field_turfs) + process_inner_turf(T) + CHECK_TICK //Really crappy lagchecks, needs improvement once someone starts using processed fields. + if(process_edge_turfs) + for(var/turf/T in edge_turfs) + process_edge_turf(T) + CHECK_TICK //Same here. + +/datum/proximity_monitor/advanced/proc/process_inner_turf(turf/T) + +/datum/proximity_monitor/advanced/proc/process_edge_turf(turf/T) + +/datum/proximity_monitor/advanced/proc/Initialize() + setup_field() + post_setup_field() + +/datum/proximity_monitor/advanced/proc/full_cleanup() //Full cleanup for when you change something that would require complete resetting. + for(var/turf/T in edge_turfs) + cleanup_edge_turf(T) + for(var/turf/T in field_turfs) + cleanup_field_turf(T) + +/datum/proximity_monitor/advanced/proc/recalculate_field(ignore_movement_check = FALSE) //Call every time the field moves (done automatically if you use update_center) or a setup specification is changed. + if(!(ignore_movement_check || ((host.loc != last_host_loc) && (field_shape != FIELD_NO_SHAPE)))) + return + update_new_turfs() + var/list/turf/needs_setup = field_turfs_new.Copy() + if(setup_field_turfs) + for(var/turf/T in field_turfs) + if(!(T in needs_setup)) + cleanup_field_turf(T) + else + needs_setup -= T + CHECK_TICK + for(var/turf/T in needs_setup) + setup_field_turf(T) + CHECK_TICK + if(setup_edge_turfs) + for(var/turf/T in edge_turfs) + cleanup_edge_turf(T) + CHECK_TICK + for(var/turf/T in edge_turfs_new) + setup_edge_turf(T) + CHECK_TICK + +/datum/proximity_monitor/advanced/proc/field_turf_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F, turf/entering) + return TRUE + +/datum/proximity_monitor/advanced/proc/field_turf_uncross(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F) + return TRUE + +/datum/proximity_monitor/advanced/proc/field_turf_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F) + return TRUE + +/datum/proximity_monitor/advanced/proc/field_turf_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_turf/F) + return TRUE + +/datum/proximity_monitor/advanced/proc/field_edge_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F, turf/entering) + return TRUE + +/datum/proximity_monitor/advanced/proc/field_edge_uncross(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F) + return TRUE + +/datum/proximity_monitor/advanced/proc/field_edge_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F) + return TRUE + +/datum/proximity_monitor/advanced/proc/field_edge_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F) + return TRUE + +/datum/proximity_monitor/advanced/HandleMove() + var/atom/_host = host + var/atom/new_host_loc = _host.loc + if(last_host_loc != new_host_loc) + recalculate_field() + +/datum/proximity_monitor/advanced/proc/post_setup_field() + +/datum/proximity_monitor/advanced/proc/setup_field() + update_new_turfs() + if(setup_field_turfs) + for(var/turf/T in field_turfs_new) + setup_field_turf(T) + CHECK_TICK + if(setup_edge_turfs) + for(var/turf/T in edge_turfs_new) + setup_edge_turf(T) + CHECK_TICK + +/datum/proximity_monitor/advanced/proc/cleanup_field_turf(turf/T) + qdel(field_turfs[T]) + field_turfs -= T + +/datum/proximity_monitor/advanced/proc/cleanup_edge_turf(turf/T) + qdel(edge_turfs[T]) + edge_turfs -= T + +/datum/proximity_monitor/advanced/proc/setup_field_turf(turf/T) + field_turfs[T] = new /obj/effect/abstract/proximity_checker/advanced/field_turf(T, src) + +/datum/proximity_monitor/advanced/proc/setup_edge_turf(turf/T) + edge_turfs[T] = new /obj/effect/abstract/proximity_checker/advanced/field_edge(T, src) + +/datum/proximity_monitor/advanced/proc/update_new_turfs() + if(!istype(host)) + return FALSE + last_host_loc = host.loc + var/turf/center = get_turf(host) + field_turfs_new = list() + edge_turfs_new = list() + switch(field_shape) + if(FIELD_NO_SHAPE) + return FALSE + if(FIELD_SHAPE_RADIUS_SQUARE) + for(var/turf/T in block(locate(center.x-current_range,center.y-current_range,center.z-square_depth_down),locate(center.x+current_range, center.y+current_range,center.z+square_depth_up))) + field_turfs_new += T + edge_turfs_new = field_turfs_new.Copy() + if(current_range >= 1) + var/list/turf/center_turfs = list() + for(var/turf/T in block(locate(center.x-current_range+1,center.y-current_range+1,center.z-square_depth_down),locate(center.x+current_range-1, center.y+current_range-1,center.z+square_depth_up))) + center_turfs += T + for(var/turf/T in center_turfs) + edge_turfs_new -= T + if(FIELD_SHAPE_CUSTOM_SQUARE) + for(var/turf/T in block(locate(center.x-square_width,center.y-square_height,center.z-square_depth_down),locate(center.x+square_width, center.y+square_height,center.z+square_depth_up))) + field_turfs_new += T + edge_turfs_new = field_turfs_new.Copy() + if(square_height >= 1 && square_width >= 1) + var/list/turf/center_turfs = list() + for(var/turf/T in block(locate(center.x-square_width+1,center.y-square_height+1,center.z-square_depth_down),locate(center.x+square_width-1, center.y+square_height-1,center.z+square_depth_up))) + center_turfs += T + for(var/turf/T in center_turfs) + edge_turfs_new -= T + +//Gets edge direction/corner, only works with square radius/WDH fields! +/datum/proximity_monitor/advanced/proc/get_edgeturf_direction(turf/T, turf/center_override = null) + var/turf/checking_from = get_turf(host) + if(istype(center_override)) + checking_from = center_override + if(field_shape != FIELD_SHAPE_RADIUS_SQUARE && field_shape != FIELD_SHAPE_CUSTOM_SQUARE) + return + if(!(T in edge_turfs)) + return + switch(field_shape) + if(FIELD_SHAPE_RADIUS_SQUARE) + if(((T.x == (checking_from.x + current_range)) || (T.x == (checking_from.x - current_range))) && ((T.y == (checking_from.y + current_range)) || (T.y == (checking_from.y - current_range)))) + return get_dir(checking_from, T) + if(T.x == (checking_from.x + current_range)) + return EAST + if(T.x == (checking_from.x - current_range)) + return WEST + if(T.y == (checking_from.y - current_range)) + return SOUTH + if(T.y == (checking_from.y + current_range)) + return NORTH + if(FIELD_SHAPE_CUSTOM_SQUARE) + if(((T.x == (checking_from.x + square_width)) || (T.x == (checking_from.x - square_width))) && ((T.y == (checking_from.y + square_height)) || (T.y == (checking_from.y - square_height)))) + return get_dir(checking_from, T) + if(T.x == (checking_from.x + square_width)) + return EAST + if(T.x == (checking_from.x - square_width)) + return WEST + if(T.y == (checking_from.y - square_height)) + return SOUTH + if(T.y == (checking_from.y + square_height)) + return NORTH + +//DEBUG FIELDS +/datum/proximity_monitor/advanced/debug + name = "\improper Color Matrix Field" + field_shape = FIELD_SHAPE_RADIUS_SQUARE + current_range = 5 + var/set_fieldturf_color = "#aaffff" + var/set_edgeturf_color = "#ffaaff" + setup_field_turfs = TRUE + setup_edge_turfs = TRUE + +/datum/proximity_monitor/advanced/debug/recalculate_field() + ..() + +/datum/proximity_monitor/advanced/debug/post_setup_field() + ..() + +/datum/proximity_monitor/advanced/debug/setup_edge_turf(turf/T) + T.color = set_edgeturf_color + ..() + +/datum/proximity_monitor/advanced/debug/cleanup_edge_turf(turf/T) + T.color = initial(T.color) + ..() + if(T in field_turfs) + T.color = set_fieldturf_color + +/datum/proximity_monitor/advanced/debug/setup_field_turf(turf/T) + T.color = set_fieldturf_color + ..() + +/datum/proximity_monitor/advanced/debug/cleanup_field_turf(turf/T) + T.color = initial(T.color) + ..() + +//DEBUG FIELD ITEM +/obj/item/device/multitool/field_debug + name = "strange multitool" + desc = "Seems to project a colored field!" + var/list/field_params = list("field_shape" = FIELD_SHAPE_RADIUS_SQUARE, "current_range" = 5, "set_fieldturf_color" = "#aaffff", "set_edgeturf_color" = "#ffaaff") + var/field_type = /datum/proximity_monitor/advanced/debug + var/operating = FALSE + var/datum/proximity_monitor/advanced/current = null + +/obj/item/device/multitool/field_debug/New() + START_PROCESSING(SSobj, src) + ..() + +/obj/item/device/multitool/field_debug/Destroy() + STOP_PROCESSING(SSobj, src) + QDEL_NULL(current) + ..() + +/obj/item/device/multitool/field_debug/proc/setup_debug_field() + var/list/new_params = field_params.Copy() + new_params["host"] = src + current = make_field(field_type, new_params) + +/obj/item/device/multitool/field_debug/attack_self(mob/user) + operating = !operating + to_chat(user, "You turn the [src] [operating? "on":"off"].") + if(!istype(current) && operating) + setup_debug_field() + else if(!operating) + QDEL_NULL(current) + +/obj/item/device/multitool/field_debug/on_mob_move() + check_turf(get_turf(src)) + +/obj/item/device/multitool/field_debug/process() + check_turf(get_turf(src)) + +/obj/item/device/multitool/field_debug/proc/check_turf(turf/T) + current.HandleMove() diff --git a/code/modules/fields/peaceborg_dampener.dm b/code/modules/fields/peaceborg_dampener.dm new file mode 100644 index 0000000000..bcf8a88585 --- /dev/null +++ b/code/modules/fields/peaceborg_dampener.dm @@ -0,0 +1,106 @@ + +//Projectile dampening field that slows projectiles and lowers their damage for an energy cost deducted every 1/5 second. +//Only use square radius for this! +/datum/proximity_monitor/advanced/peaceborg_dampener + name = "\improper Hyperkinetic Dampener Field" + requires_processing = TRUE + setup_edge_turfs = TRUE + setup_field_turfs = TRUE + field_shape = FIELD_SHAPE_RADIUS_SQUARE + var/static/image/edgeturf_south = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_south") + var/static/image/edgeturf_north = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_north") + var/static/image/edgeturf_west = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_west") + var/static/image/edgeturf_east = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_east") + var/static/image/northwest_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northwest") + var/static/image/southwest_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southwest") + var/static/image/northeast_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northeast") + var/static/image/southeast_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southeast") + var/obj/item/borg/projectile_dampen/projector = null + var/list/obj/item/projectile/tracked + var/list/obj/item/projectile/staging + +/datum/proximity_monitor/advanced/peaceborg_dampener/New() + tracked = list() + staging = list() + ..() + +/datum/proximity_monitor/advanced/peaceborg_dampener/process() + if(!istype(projector)) + qdel(src) + var/list/ranged = list() + for(var/obj/item/projectile/P in range(current_range, get_turf(host))) + ranged += P + for(var/obj/item/projectile/P in tracked) + if(!(P in ranged) || !P.loc) + release_projectile(P) + for(var/mob/living/silicon/robot/R in range(current_range, get_turf(host))) + if(R.has_buckled_mobs()) + for(var/mob/living/L in R.buckled_mobs) + L.visible_message("[L] is knocked off of [R] by the charge in [R]'s chassis induced by [name]!") //I know it's bad. + L.Weaken(3) + R.unbuckle_mob(L) + do_sparks(5, 0, L) + ..() + +/datum/proximity_monitor/advanced/peaceborg_dampener/setup_edge_turf(turf/T) + ..() + var/image/I = get_edgeturf_overlay(get_edgeturf_direction(T)) + var/obj/effect/abstract/proximity_checker/advanced/F = edge_turfs[T] + F.appearance = I.appearance + F.invisibility = 0 + F.layer = 5 + +/datum/proximity_monitor/advanced/peaceborg_dampener/cleanup_edge_turf(turf/T) + ..() + +/datum/proximity_monitor/advanced/peaceborg_dampener/proc/get_edgeturf_overlay(direction) + switch(direction) + if(NORTH) + return edgeturf_north + if(SOUTH) + return edgeturf_south + if(EAST) + return edgeturf_east + if(WEST) + return edgeturf_west + if(NORTHEAST) + return northeast_corner + if(NORTHWEST) + return northwest_corner + if(SOUTHEAST) + return southeast_corner + if(SOUTHWEST) + return southwest_corner + +/datum/proximity_monitor/advanced/peaceborg_dampener/proc/capture_projectile(obj/item/projectile/P, track_projectile = TRUE) + if(P in tracked) + return + projector.dampen_projectile(P, track_projectile) + if(track_projectile) + tracked += P + +/datum/proximity_monitor/advanced/peaceborg_dampener/proc/release_projectile(obj/item/projectile/P) + projector.restore_projectile(P) + tracked -= P + +/datum/proximity_monitor/advanced/peaceborg_dampener/field_edge_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F) + if(!is_turf_in_field(get_turf(AM), src)) + if(istype(AM, /obj/item/projectile)) + if(AM in tracked) + release_projectile(AM) + else + capture_projectile(AM, FALSE) + return ..() + +/datum/proximity_monitor/advanced/peaceborg_dampener/field_edge_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F) + if(istype(AM, /obj/item/projectile) && !(AM in tracked) && staging[AM] && !is_turf_in_field(staging[AM], src)) + capture_projectile(AM) + staging -= AM + return ..() + +/datum/proximity_monitor/advanced/peaceborg_dampener/field_edge_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/field_edge/F, turf/entering) + if(istype(AM, /obj/item/projectile)) + staging[AM] = get_turf(AM) + . = ..() + if(!.) + staging -= AM //This one ain't goin' through. diff --git a/code/modules/fields/turf_objects.dm b/code/modules/fields/turf_objects.dm new file mode 100644 index 0000000000..683fa67c96 --- /dev/null +++ b/code/modules/fields/turf_objects.dm @@ -0,0 +1,76 @@ + +/obj/effect/abstract/proximity_checker/advanced + name = "field" + desc = "Why can you see energy fields?!" + icon = null + icon_state = null + alpha = 0 + invisibility = INVISIBILITY_ABSTRACT + flags = ABSTRACT|ON_BORDER + var/datum/proximity_monitor/advanced/parent = null + +/obj/effect/abstract/proximity_checker/advanced/Initialize(mapload, _monitor) + if(_monitor) + parent = _monitor + return ..() + +/obj/effect/abstract/proximity_checker/advanced/center + name = "field anchor" + desc = "No." + +/obj/effect/abstract/proximity_checker/advanced/field_turf + name = "energy field" + desc = "Get off my turf!" + +/obj/effect/abstract/proximity_checker/advanced/field_turf/CanPass(atom/movable/AM, turf/target, height) + if(parent) + return parent.field_turf_canpass(AM, src, target) + return TRUE + +/obj/effect/abstract/proximity_checker/advanced/field_turf/Crossed(atom/movable/AM) + if(parent) + return parent.field_turf_crossed(AM, src) + return TRUE + +/obj/effect/abstract/proximity_checker/advanced/field_turf/Uncross(atom/movable/AM) + if(parent) + return parent.field_turf_uncross(AM, src) + return TRUE + +/obj/effect/abstract/proximity_checker/advanced/field_turf/Uncrossed(atom/movable/AM) + if(parent) + return parent.field_turf_uncrossed(AM, src) + return TRUE + +/obj/effect/abstract/proximity_checker/advanced/field_edge + name = "energy field edge" + desc = "Edgy description here." + +/obj/effect/abstract/proximity_checker/advanced/field_edge/CanPass(atom/movable/AM, turf/target, height) + if(parent) + return parent.field_edge_canpass(AM, src, target) + return TRUE + +/obj/effect/abstract/proximity_checker/advanced/field_edge/Crossed(atom/movable/AM) + if(parent) + return parent.field_edge_crossed(AM, src) + return TRUE + +/obj/effect/abstract/proximity_checker/advanced/field_edge/Uncross(atom/movable/AM) + if(parent) + return parent.field_edge_uncross(AM, src) + return TRUE + +/obj/effect/abstract/proximity_checker/advanced/field_edge/Uncrossed(atom/movable/AM) + if(parent) + return parent.field_edge_uncrossed(AM, src) + return TRUE + +/proc/is_turf_in_field(turf/T, datum/proximity_monitor/advanced/F) //Looking for ways to optimize this! + for(var/obj/effect/abstract/proximity_checker/advanced/O in T) + if(istype(O, /obj/effect/abstract/proximity_checker/advanced/field_edge)) + if(O.parent == F) + return FIELD_EDGE + if(O.parent == F) + return FIELD_TURF + return NO_FIELD diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index f3cf0f1c5f..4fbe56361e 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -407,7 +407,8 @@ /obj/item/weapon/reagent_containers/borghypo/peace, /obj/item/weapon/holosign_creator/cyborg, /obj/item/borg/cyborghug/peacekeeper, - /obj/item/weapon/extinguisher) + /obj/item/weapon/extinguisher, + /obj/item/borg/projectile_dampen) emag_modules = list(/obj/item/weapon/reagent_containers/borghypo/peace/hacked) ratvar_modules = list( /obj/item/clockwork/slab/cyborg/peacekeeper, diff --git a/icons/effects/fields.dmi b/icons/effects/fields.dmi new file mode 100644 index 0000000000000000000000000000000000000000..5eadb96a1a01822a7a37b9c9b70d2efce0db2d06 GIT binary patch literal 1939 zcma)-dpy&P9>;$hTiS+6xoc5QJVo@{BdqM=-V=!(L{V~SL@@z{i zJ-L+@dn8X{s$*7@%O)n5jhanMxomU1&g=Yt&L7{;=kLUxZ%IhRaIB9jsm0Dw|m$7|KevEL5ib;$OG zpRwvIxM-N`fRMxQGjzjpZAqwY8xz+~=CeOys%PfPC>;}momEYFhP^X6UU}jky;pOH zFk%P|7m|GWZHCXIyZ&ceU&-meeLR-?ZZJ>Ta^e&C`XvVjVQXZ;nooxMFYj0fl%)_+ z{<2bb@L7G!lG)lJ31gt8$ z_YZ|uV*vnK=HrehQmUoZ0|`QJ1gC+B5ADW<&FuIdq-k9_E*1B8NZ=A@CDd7#GJ5%P zEACxbVTP?E2!Y0%v~MXr=zGZ%#d>wAA7y{O*xQ6FZ#L#4K74r_^MoC4*IY10{yAcQ zb3x?e%E6J~UNXD#KzXnn+|44!uk(jrF!`md_>nhPt1kxq4i@O}`Z|GvyNlNL5i45s z0+vRb!vX5O3P~ZLcv=Khp2y%!6=3U!OlI7!(k7yFIqAFVUt4cryDd6aR_5Kl$R^b)@p=f$ zEN3jPJ~Uf=AQx@MBgRTXwZjU1sg0T~{08jfLG6(V;>hBdaX{>$;q0!41CTkA;(NDk-|#Mr-sxkGn}frSccixsSCffd*HYs3#+Pa(eeXg#DsP!Gb0J0)S=^LeH?O!YV;w8LKq7JIQ zNtsC&UjZYb^|Gm;On^q3gDCldV#PC9wpYIJswYVOJwJ}oALzbug4(6>twpL6_H|7g zL0n=XimBp+HD0`kQS50!m72PKJG}2U0azCO2$9qn`2D?+kz{ha$l|b0jb$Z^8QAv% zZts@}@_zfsf$@3q>OEx_5BEg+^`M8#VCd))TBK2|(PKf+fGJCo5njA*M^O zpVKba{OJ+8SH`y?+urszFOs`Bgp7MtkPm>Q^gm2t5e(i3w>Q;5N$dwhah2(KUtH*0 z4P3nG_FZa&_l1lb+g4<~t8;VTUExi2?^?+NzBkF*kobRcC9RvMd-9Lxn$$ICt@zrZ z#Pg=Zw4*Hl>Fr3taH-R`;(o7dKVp|=4b;*Nj%qUNy3D%olX=(iJ8ME+Bodg!$SlFs zMIpy*{tJCTg}!_y8J6@TMBQ~U6X((Qtn`n~Uf?g(M#U9X17ZV+GwweJ zqO!FfASqLvUM0HGcbc>xK)Q;{kN!*VFxEeZ0|rv{{4*#FOzqIB4OjJRwZ4%a2_lpd@RX?er^lrSIF>QBcku!hDKb z5@tOLntz7?suU+@oeqp?NegOW<{cW9zvsVH^%NVVRVeMRzn|wn&>*vkiL|x+mT8_8 v3j_HFY&a2xWlgC&Yk;;ZqzLf^xabf{dlp-JWvzen%K| Date: Thu, 18 May 2017 16:36:54 -0500 Subject: [PATCH 06/52] Fixes obtaining blob chems --- code/modules/food_and_drinks/kitchen_machinery/gibber.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm index 81e3692376..f4ffb4123a 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm @@ -174,7 +174,6 @@ var/mob/living/carbon/human/gibee = occupant sourcejob = gibee.job var/sourcenutriment = mob_occupant.nutrition / 15 - var/sourcetotalreagents = mob_occupant.reagents.total_volume var/gibtype = /obj/effect/decal/cleanable/blood/gibs var/typeofmeat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human var/typeofskin = /obj/item/stack/sheet/animalhide/human @@ -207,7 +206,6 @@ newmeat.reagents.add_reagent ("nutriment", sourcenutriment / meat_produced) // Thehehe. Fat guys go first if(sourcejob) newmeat.subjectjob = sourcejob - src.occupant.reagents.trans_to (newmeat, round (sourcetotalreagents / meat_produced, 1)) // Transfer all the reagents from the allmeat[i] = newmeat allskin = newskin From 90d96dba065dd171330883c1752b03c1694fc247 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:36:58 -0500 Subject: [PATCH 07/52] Fixes some holocall issues --- code/datums/holocall.dm | 11 ++++++++--- code/game/machinery/hologram.dm | 2 -- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index 10599ca25b..8f18f8b1fe 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -31,6 +31,7 @@ var/obj/machinery/holopad/H = I if(!QDELETED(H) && H.is_operational()) dialed_holopads += H + H.say("Incoming call.") LAZYADD(H.holo_calls, src) if(!dialed_holopads.len) @@ -42,12 +43,16 @@ //cleans up ALL references :) /datum/holocall/Destroy() - QDEL_NULL(eye) - user.reset_perspective() + if(user.client) + for(var/datum/camerachunk/chunk in eye.visibleCameraChunks) + user.client.images -= chunk.obscured + user.remote_control = null + QDEL_NULL(eye) user = null - hologram.HC = null + if(hologram) + hologram.HC = null hologram = null calling_holopad.outgoing_call = null diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 2c2e39c4d7..73b00b864e 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -26,8 +26,6 @@ Possible to do for anyone motivated enough: #define HOLOPAD_PASSIVE_POWER_USAGE 1 #define HOLOGRAM_POWER_USAGE 2 - -GLOBAL_LIST_EMPTY(holopads) #define HOLOPAD_MODE RANGE_BASED /obj/machinery/holopad From fe89b9ea8a173d4ec84652dc0aa1c23133422a8c Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:02 -0500 Subject: [PATCH 08/52] Fixes bane runtimes --- code/modules/mob/living/living.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 926e88787e..99c99a44d9 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -823,7 +823,7 @@ /mob/living/proc/has_bane(banetype) var/datum/antagonist/devil/devilInfo = is_devil(src) - return (banetype == devilInfo.bane) + return devilInfo && banetype == devilInfo.bane /mob/living/proc/check_weakness(obj/item/weapon, mob/living/attacker) if(mind && mind.has_antag_datum(ANTAG_DATUM_DEVIL)) From 552433143e3e6ee73773098e529e54006dc4f643 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:07 -0500 Subject: [PATCH 09/52] Clowns get an automatic self-recharging pie cannon as a traitor item for 10TC --- .../objects/items/weapons/pneumaticCannon.dm | 21 ++++++++++++++++++- code/modules/uplink/uplink_item.dm | 8 +++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/weapons/pneumaticCannon.dm b/code/game/objects/items/weapons/pneumaticCannon.dm index 86c517aa99..64fb4c7ecc 100644 --- a/code/game/objects/items/weapons/pneumaticCannon.dm +++ b/code/game/objects/items/weapons/pneumaticCannon.dm @@ -244,7 +244,7 @@ range_multiplier = 3 fire_mode = PCANNON_FIFO throw_amount = 1 - maxWeightClass = 100 //50 pies. :^) + maxWeightClass = 150 //50 pies. :^) clumsyCheck = FALSE /obj/item/weapon/pneumatic_cannon/pie/can_load_item(obj/item/I, mob/user) @@ -252,3 +252,22 @@ return ..() to_chat(user, "[src] only accepts pies!") return FALSE + +/obj/item/weapon/pneumatic_cannon/pie/selfcharge + automatic = TRUE + var/charge_amount = 1 + var/charge_ticks = 1 + var/charge_tick = 0 + maxWeightClass = 60 //20 pies. + +/obj/item/weapon/pneumatic_cannon/pie/selfcharge/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/item/weapon/pneumatic_cannon/pie/selfcharge/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/weapon/pneumatic_cannon/pie/selfcharge/process() + if(++charge_tick >= charge_ticks) + fill_with_type(/obj/item/weapon/reagent_containers/food/snacks/pie, charge_amount) diff --git a/code/modules/uplink/uplink_item.dm b/code/modules/uplink/uplink_item.dm index cbee8e53f2..d6e6eae1ca 100644 --- a/code/modules/uplink/uplink_item.dm +++ b/code/modules/uplink/uplink_item.dm @@ -1276,6 +1276,14 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. restricted_roles = list("Chaplain") surplus = 5 //Very low chance to get it in a surplus crate even without being the chaplain +/datum/uplink_item/role_restricted/pie_cannon + name = "Banana Cream Pie Cannon" + desc = "A special pie cannon for a special clown, this gadget can hold up to 20 pies and automatically fabricates one every two seconds!" + cost = 10 + item = /obj/item/weapon/pneumatic_cannon/pie/selfcharge + restricted_roles = list("Clown") + surplus = 0 //No fun unless you're the clown! + /datum/uplink_item/role_restricted/ancient_jumpsuit name = "Ancient Jumpsuit" desc = "A tattered old jumpsuit that will provide absolutely no benefit to you. It fills the wearer with a strange compulsion to blurt out 'glorf'." From 9896c989357a086b7d47f4d9b23565c81fdac145 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:11 -0500 Subject: [PATCH 10/52] Fixes bypassing cargo export values on minerals --- code/game/machinery/bank_machine.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm index ace9b543d1..0ebae75da6 100644 --- a/code/game/machinery/bank_machine.dm +++ b/code/game/machinery/bank_machine.dm @@ -25,9 +25,6 @@ if(istype(I, /obj/item/stack/spacecash)) var/obj/item/stack/spacecash/C = I value = C.value * C.amount - if(istype(I, /obj/item/weapon/coin)) - var/obj/item/weapon/coin/C = I - value = C.value if(value) SSshuttle.points += value to_chat(user, "You deposit [I]. The station now has [SSshuttle.points] credits.") From 670a6f2126bfa57b16b8c08e7dc3f2ce942681cc Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:18 -0500 Subject: [PATCH 11/52] Vending machines are more dangerous when throwing items --- code/game/machinery/vending.dm | 28 +++++-- code/game/objects/items.dm | 3 + .../objects/items/weapons/cigs_lighters.dm | 83 ++++++++++--------- .../objects/items/weapons/grenades/grenade.dm | 32 +++---- 4 files changed, 90 insertions(+), 56 deletions(-) diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index bd51e1e651..ebe4c79730 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -48,6 +48,7 @@ var/icon_deny //Icon_state when vending! var/seconds_electrified = 0 //Shock customers like an airlock. var/shoot_inventory = 0 //Fire items at customers! We're broken! + var/shoot_inventory_chance = 2 var/shut_up = 0 //Stop spouting those godawful pitches! var/extended_inventory = 0 //can we access the hidden inventory? var/scan_id = 1 @@ -561,7 +562,7 @@ speak(slogan) last_slogan = world.time - if(shoot_inventory && prob(2)) + if(shoot_inventory && prob(shoot_inventory_chance)) throw_item() @@ -592,7 +593,7 @@ if(!target) return 0 - for(var/datum/data/vending_product/R in product_records) + for(var/datum/data/vending_product/R in shuffle(product_records)) if(R.amount <= 0) //Try to use a record that actually has something to dump. continue var/dump_path = R.product_path @@ -605,19 +606,22 @@ if(!throw_item) return 0 + pre_throw(throw_item) + throw_item.throw_at(target, 16, 3) visible_message("[src] launches [throw_item] at [target]!") return 1 +/obj/machinery/vending/proc/pre_throw(obj/item/I) + return + /obj/machinery/vending/proc/shock(mob/user, prb) if(stat & (BROKEN|NOPOWER)) // unpowered, no shock return FALSE if(!prob(prb)) return FALSE - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() + do_sparks(5, TRUE, src) var/tmp/check_range = TRUE if(electrocute_mob(user, get_area(src), src, 0.7, check_range)) return TRUE @@ -864,6 +868,11 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/item/weapon/storage/fancy/cigarettes/cigars = 1, /obj/item/weapon/storage/fancy/cigarettes/cigars/havana = 1, /obj/item/weapon/storage/fancy/cigarettes/cigars/cohiba = 1) refill_canister = /obj/item/weapon/vending_refill/cigarette +/obj/machinery/vending/cigarette/pre_throw(obj/item/I) + if(istype(I, /obj/item/weapon/lighter)) + var/obj/item/weapon/lighter/L = I + L.set_lit(TRUE) + /obj/machinery/vending/medical name = "\improper NanoMed Plus" desc = "Medical drug dispenser." @@ -919,6 +928,15 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) resistance_flags = FIRE_PROOF +/obj/machinery/vending/security/pre_throw(obj/item/I) + if(istype(I, /obj/item/weapon/grenade)) + var/obj/item/weapon/grenade/G = I + G.preprime() + else if(istype(I, /obj/item/device/flashlight)) + var/obj/item/device/flashlight/F = I + F.on = TRUE + F.update_brightness() + /obj/machinery/vending/hydronutrients name = "\improper NutriMax" desc = "A plant nutrients vendor." diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 8dc9bd89b2..4a69fdff87 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -528,6 +528,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) /obj/item/throw_impact(atom/A) if(A && !QDELETED(A)) + if(is_hot() && isliving(A)) + var/mob/living/L = A + L.IgniteMob() var/itempush = 1 if(w_class < 4) itempush = 0 //too light to push anything diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm index 3cf5305dcb..82229bb7cf 100644 --- a/code/game/objects/items/weapons/cigs_lighters.dm +++ b/code/game/objects/items/weapons/cigs_lighters.dm @@ -472,8 +472,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM flags = CONDUCT slot_flags = SLOT_BELT var/lit = 0 + var/fancy = TRUE heat = 1500 resistance_flags = FIRE_PROOF + light_color = LIGHT_COLOR_FIRE /obj/item/weapon/lighter/update_icon() if(lit) @@ -484,37 +486,28 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/weapon/lighter/ignition_effect(atom/A, mob/user) . = "With a single flick of their wrist, [user] smoothly lights [A] with [src]. Damn [user.p_theyre()] cool." -/obj/item/weapon/lighter/greyscale - name = "cheap lighter" - desc = "A cheap-as-free lighter." - icon_state = "lighter" - -/obj/item/weapon/lighter/greyscale/Initialize() - . = ..() - add_atom_colour(color2hex(randomColor(1)), FIXED_COLOUR_PRIORITY) - update_icon() - -/obj/item/weapon/lighter/greyscale/update_icon() - cut_overlays() - var/mutable_appearance/base_overlay = mutable_appearance(icon,"[initial(icon_state)]_base") - base_overlay.appearance_flags = RESET_COLOR //the edging doesn't change color +/obj/item/weapon/lighter/proc/set_lit(new_lit) + lit = new_lit if(lit) - base_overlay.icon_state = "[initial(icon_state)]_on" - add_overlay(base_overlay) - -/obj/item/weapon/lighter/greyscale/ignition_effect(atom/A, mob/user) - . = "After some fiddling, [user] manages to light [A] with [src]." + force = 5 + damtype = "fire" + hitsound = 'sound/items/welder.ogg' + attack_verb = list("burnt", "singed") + set_light(1) + START_PROCESSING(SSobj, src) + else + hitsound = "swing_hit" + force = 0 + attack_verb = null //human_defense.dm takes care of it + set_light(0) + STOP_PROCESSING(SSobj, src) + update_icon() /obj/item/weapon/lighter/attack_self(mob/living/user) if(user.is_holding(src)) if(!lit) - lit = 1 - update_icon() - force = 5 - damtype = "fire" - hitsound = 'sound/items/welder.ogg' - attack_verb = list("burnt", "singed") - if(!istype(src, /obj/item/weapon/lighter/greyscale)) + set_lit(TRUE) + if(fancy) user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.", "Without even breaking stride, you flip open and lights [src] in one smooth movement.") else var/prot = FALSE @@ -534,20 +527,12 @@ CIGARETTE PACKETS ARE IN FANCY.DM user.apply_damage(5, BURN, hitzone) user.visible_message("After a few attempts, [user] manages to light [src] - however, [user.p_they()] burn their finger in the process.", "You burn yourself while lighting the lighter!") - set_light(1) - START_PROCESSING(SSobj, src) else - lit = 0 - update_icon() - hitsound = "swing_hit" - force = 0 - attack_verb = null //human_defense.dm takes care of it - if(!istype(src, /obj/item/weapon/lighter/greyscale)) + set_lit(FALSE) + if(fancy) user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what [user.p_theyre()] doing. Wow.", "You quietly shut off [src] without even looking at what you're doing. Wow.") else user.visible_message("[user] quietly shuts off [src].", "You quietly shut off [src].") - set_light(0) - STOP_PROCESSING(SSobj, src) else . = ..() @@ -562,7 +547,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(M == user) cig.attackby(src, user) else - if(!istype(src, /obj/item/weapon/lighter/greyscale)) + if(fancy) cig.light("[user] whips the [name] out and holds it for [M]. [user.p_their(TRUE)] arm is as steady as the unflickering flame they light \the [cig] with.") else cig.light("[user] holds the [name] out for [M], and lights the [cig.name].") @@ -575,6 +560,30 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/weapon/lighter/is_hot() return lit * heat + +/obj/item/weapon/lighter/greyscale + name = "cheap lighter" + desc = "A cheap-as-free lighter." + icon_state = "lighter" + fancy = FALSE + +/obj/item/weapon/lighter/greyscale/Initialize() + . = ..() + add_atom_colour(color2hex(randomColor(1)), FIXED_COLOUR_PRIORITY) + update_icon() + +/obj/item/weapon/lighter/greyscale/update_icon() + cut_overlays() + var/mutable_appearance/base_overlay = mutable_appearance(icon,"[initial(icon_state)]_base") + base_overlay.appearance_flags = RESET_COLOR //the edging doesn't change color + if(lit) + base_overlay.icon_state = "[initial(icon_state)]_on" + add_overlay(base_overlay) + +/obj/item/weapon/lighter/greyscale/ignition_effect(atom/A, mob/user) + . = "After some fiddling, [user] manages to light [A] with [src]." + + /////////// //ROLLING// /////////// diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index e478813558..a5f5eb6133 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -48,23 +48,27 @@ /obj/item/weapon/grenade/attack_self(mob/user) if(!active) if(clown_check(user)) - to_chat(user, "You prime the [name]! [det_time/10] seconds!") - playsound(user.loc, 'sound/weapons/armbomb.ogg', 60, 1) - active = 1 - icon_state = initial(icon_state) + "_active" - add_fingerprint(user) - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - var/message = "[ADMIN_LOOKUPFLW(user)]) has primed a [name] for detonation at [ADMIN_COORDJMP(bombturf)]" - GLOB.bombers += message - message_admins(message) - log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] [COORD(bombturf)].") + preprime(user) if(iscarbon(user)) var/mob/living/carbon/C = user C.throw_mode_on() - spawn(det_time) - prime() +/obj/item/weapon/grenade/proc/preprime(mob/user) + if(user) + to_chat(user, "You prime the [name]! [det_time/10] seconds!") + playsound(loc, 'sound/weapons/armbomb.ogg', 60, 1) + active = TRUE + icon_state = initial(icon_state) + "_active" + add_fingerprint(user) + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + if(user) + var/message = "[ADMIN_LOOKUPFLW(user)]) has primed a [name] for detonation at [ADMIN_COORDJMP(bombturf)]" + GLOB.bombers += message + message_admins(message) + log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] [COORD(bombturf)].") + + addtimer(CALLBACK(src, .proc/prime), det_time) /obj/item/weapon/grenade/proc/prime() @@ -104,4 +108,4 @@ if(damage && attack_type == PROJECTILE_ATTACK && prob(15)) owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!") prime() - return 1 //It hit the grenade, not them \ No newline at end of file + return 1 //It hit the grenade, not them From 79655b17e5b80e6367263a85f873431519158839 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:22 -0500 Subject: [PATCH 12/52] Quick fix for AI language issues. --- code/game/machinery/hologram.dm.rej | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 code/game/machinery/hologram.dm.rej diff --git a/code/game/machinery/hologram.dm.rej b/code/game/machinery/hologram.dm.rej new file mode 100644 index 0000000000..fbb5cbc866 --- /dev/null +++ b/code/game/machinery/hologram.dm.rej @@ -0,0 +1,10 @@ +diff a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm (rejected hunks) +@@ -295,7 +295,7 @@ GLOBAL_LIST_EMPTY(holopads) + Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY) + Hologram.Impersonation = user + +- Hologram.language_holder = user.get_language_holder() ++ Hologram.copy_known_languages_from(user,replace = TRUE) + Hologram.mouse_opacity = 0//So you can't click on it. + Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. + Hologram.anchored = 1//So space wind cannot drag it. From bbcc6f9be9f532c297ec5328fd996f6a99b8e308 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:26 -0500 Subject: [PATCH 13/52] Proselytizing alloy shards will now proselytize all the shards in the tile --- .../clock_helpers/proselytizer_helpers.dm | 127 ++++++++++-------- .../clock_items/clock_components.dm | 12 +- .../clock_items/clockwork_proselytizer.dm | 35 +++-- icons/obj/clockwork_objects.dmi | Bin 120504 -> 120392 bytes 4 files changed, 104 insertions(+), 70 deletions(-) diff --git a/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm b/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm index 529f38d832..0566fb2d7a 100644 --- a/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm +++ b/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm @@ -5,48 +5,48 @@ //otherwise, return literally any non-list thing but preferably FALSE //returning TRUE won't produce the "cannot be proselytized" message and will still prevent proselytizing -/atom/proc/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/atom/proc/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Turf conversion -/turf/closed/wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //four sheets of metal +/turf/closed/wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) //four sheets of metal return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 4), "spawn_dir" = SOUTH) -/turf/closed/wall/mineral/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal +/turf/closed/wall/mineral/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) //two sheets of metal return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2), "spawn_dir" = SOUTH) -/turf/closed/wall/mineral/iron/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal, five rods +/turf/closed/wall/mineral/iron/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) //two sheets of metal, five rods return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2) - (POWER_ROD * 5), "spawn_dir" = SOUTH) -/turf/closed/wall/mineral/cult/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //no metal +/turf/closed/wall/mineral/cult/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) //no metal return list("operation_time" = 80, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL, "spawn_dir" = SOUTH) -/turf/closed/wall/shuttle/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal +/turf/closed/wall/shuttle/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) //two sheets of metal return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2), "spawn_dir" = SOUTH) -/turf/closed/wall/r_wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/turf/closed/wall/r_wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE -/turf/closed/wall/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/turf/closed/wall/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return list("operation_time" = 50, "new_obj_type" = /turf/open/floor/clockwork, "power_cost" = -POWER_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH) -/turf/open/floor/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/turf/open/floor/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) if(floor_tile == /obj/item/stack/tile/plasteel) new floor_tile(src) make_plating() playsound(src, 'sound/items/Crowbar.ogg', 10, 1) //clink return list("operation_time" = 30, "new_obj_type" = /turf/open/floor/clockwork, "power_cost" = POWER_FLOOR, "spawn_dir" = SOUTH) -/turf/open/floor/plating/asteroid/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/turf/open/floor/plating/asteroid/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE -/turf/open/floor/plating/ashplanet/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/turf/open/floor/plating/ashplanet/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE -/turf/open/floor/plating/lava/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/turf/open/floor/plating/lava/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE -/turf/open/floor/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/turf/open/floor/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) if(locate(/obj/structure/table) in src) return FALSE if(is_blocked_turf(src, TRUE)) @@ -58,7 +58,7 @@ return list("operation_time" = operation_time, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH) //False wall conversion -/obj/structure/falsewall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/falsewall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) var/cost = POWER_WALL_MINUS_FLOOR if(ispath(mineral, /obj/item/stack/sheet/metal)) cost -= (POWER_METAL * (2 + mineral_amount)) //four sheets of metal, plus an assumption that the girder is also two @@ -66,17 +66,17 @@ cost -= (POWER_METAL * 2) //anything that doesn't use metal just has the girder return list("operation_time" = 50, "new_obj_type" = /obj/structure/falsewall/brass, "power_cost" = cost, "spawn_dir" = SOUTH) -/obj/structure/falsewall/iron/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) //two sheets of metal, two rods; special assumption +/obj/structure/falsewall/iron/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) //two sheets of metal, two rods; special assumption return list("operation_time" = 50, "new_obj_type" = /obj/structure/falsewall/brass, "power_cost" = POWER_WALL_MINUS_FLOOR - (POWER_METAL * 2) - (POWER_ROD * 2), "spawn_dir" = SOUTH) -/obj/structure/falsewall/reinforced/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/falsewall/reinforced/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE -/obj/structure/falsewall/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/falsewall/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Metal conversion -/obj/item/stack/tile/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/item/stack/tile/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) if(source) return FALSE var/amount_temp = get_amount() @@ -104,7 +104,7 @@ to_chat(user, "You need at least 20 floor tiles to convert into brass.") return TRUE -/obj/item/stack/rods/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/item/stack/rods/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) if(source) return FALSE if(proselytizer.metal_to_power) @@ -122,7 +122,7 @@ to_chat(user, "You need at least 10 rods to convert into brass.") return TRUE -/obj/item/stack/sheet/metal/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/item/stack/sheet/metal/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) if(source) return FALSE if(proselytizer.metal_to_power) @@ -140,7 +140,7 @@ to_chat(user, "You need at least 5 sheets of metal to convert into brass.") return TRUE -/obj/item/stack/sheet/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/item/stack/sheet/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) if(source) return FALSE if(proselytizer.metal_to_power) @@ -159,23 +159,23 @@ return TRUE //Brass directly to power -/obj/item/stack/tile/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/item/stack/tile/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) if(source) return FALSE return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "power_cost" = -(amount*POWER_FLOOR), "spawn_dir" = SOUTH) //Airlock conversion -/obj/machinery/door/airlock/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/machinery/door/airlock/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) var/doortype = /obj/machinery/door/airlock/clockwork if(glass) doortype = /obj/machinery/door/airlock/clockwork/brass return list("operation_time" = 60, "new_obj_type" = doortype, "power_cost" = POWER_WALL_TOTAL, "spawn_dir" = dir) -/obj/machinery/door/airlock/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/machinery/door/airlock/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Table conversion -/obj/structure/table/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/table/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) var/prosel_cost = POWER_STANDARD if(framestack == /obj/item/stack/rods) prosel_cost -= POWER_ROD*framestackamount @@ -187,10 +187,10 @@ prosel_cost -= POWER_PLASTEEL*buildstackamount return list("operation_time" = 20, "new_obj_type" = /obj/structure/table/reinforced/brass, "power_cost" = prosel_cost, "spawn_dir" = SOUTH) -/obj/structure/table/reinforced/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/table/reinforced/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE -/obj/structure/table_frame/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/table_frame/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) var/prosel_cost = POWER_FLOOR if(framestack == /obj/item/stack/rods) prosel_cost -= POWER_ROD*framestackamount @@ -198,11 +198,11 @@ prosel_cost -= POWER_FLOOR*framestackamount return list("operation_time" = 10, "new_obj_type" = /obj/structure/table_frame/brass, "power_cost" = prosel_cost, "spawn_dir" = SOUTH) -/obj/structure/table_frame/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/table_frame/brass/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Window conversion -/obj/structure/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) var/windowtype = /obj/structure/window/reinforced/clockwork var/new_dir = TRUE var/prosel_time = 15 @@ -220,18 +220,18 @@ INVOKE_ASYNC(proselytizer, /obj/item/clockwork/clockwork_proselytizer.proc/proselytize, G, user) return list("operation_time" = prosel_time, "new_obj_type" = windowtype, "power_cost" = prosel_cost, "spawn_dir" = dir, "dir_in_new" = new_dir) -/obj/structure/window/reinforced/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/window/reinforced/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Windoor conversion -/obj/machinery/door/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/machinery/door/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return list("operation_time" = 30, "new_obj_type" = /obj/machinery/door/window/clockwork, "power_cost" = POWER_STANDARD, "spawn_dir" = dir, "dir_in_new" = TRUE) -/obj/machinery/door/window/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/machinery/door/window/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Grille conversion -/obj/structure/grille/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/grille/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) var/grilletype = /obj/structure/grille/ratvar var/prosel_time = 15 if(broken) @@ -239,32 +239,32 @@ prosel_time = 5 return list("operation_time" = prosel_time, "new_obj_type" = grilletype, "power_cost" = 0, "spawn_dir" = dir) -/obj/structure/grille/ratvar/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/grille/ratvar/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Lattice conversion -/obj/structure/lattice/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/lattice/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return list("operation_time" = 0, "new_obj_type" = /obj/structure/lattice/clockwork, "power_cost" = 0, "spawn_dir" = SOUTH, "no_target_deletion" = TRUE) -/obj/structure/lattice/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/lattice/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) ratvar_act() //just in case we're the wrong type for some reason?? return FALSE -/obj/structure/lattice/catwalk/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/lattice/catwalk/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return list("operation_time" = 0, "new_obj_type" = /obj/structure/lattice/catwalk/clockwork, "power_cost" = 0, "spawn_dir" = SOUTH, "no_target_deletion" = TRUE) -/obj/structure/lattice/catwalk/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/lattice/catwalk/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) return FALSE //Girder conversion -/obj/structure/girder/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/girder/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) var/prosel_cost = POWER_GEAR - (POWER_METAL * 2) if(state == GIRDER_REINF_STRUTS || state == GIRDER_REINF) prosel_cost -= POWER_PLASTEEL return list("operation_time" = 20, "new_obj_type" = /obj/structure/destructible/clockwork/wall_gear, "power_cost" = prosel_cost, "spawn_dir" = SOUTH) //Hitting a clockwork structure will try to repair it. -/obj/structure/destructible/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/structure/destructible/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) . = TRUE var/list/repair_values = list() if(!proselytizer.proselytizer_repair_checks(repair_values, src, user)) @@ -287,7 +287,7 @@ "You finish repairing [src]. It is now at [obj_integrity]/[max_integrity] integrity.") //Hitting a sigil of transmission will try to charge from it. -/obj/effect/clockwork/sigil/transmission/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/obj/effect/clockwork/sigil/transmission/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) . = TRUE var/list/charge_values = list() if(!proselytizer.sigil_charge_checks(charge_values, src, user)) @@ -337,7 +337,7 @@ adjustHealth(-amount) //Hitting a ratvar'd silicon will also try to repair it. -/mob/living/silicon/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/mob/living/silicon/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) . = TRUE if(health == maxHealth) //if we're at maximum health, prosel the turf under us return FALSE @@ -346,7 +346,7 @@ "You finish repairin[src == user ? "g yourself. You are":"g [src]. [p_they(TRUE)] [p_are()]"] now at [abs(HEALTH_THRESHOLD_DEAD - health)]/[abs(HEALTH_THRESHOLD_DEAD - maxHealth)] health.") //Same with clockwork mobs. -/mob/living/simple_animal/hostile/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/mob/living/simple_animal/hostile/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) . = TRUE if(health == maxHealth) //if we're at maximum health, prosel the turf under us return FALSE @@ -355,7 +355,7 @@ "You finish repairin[src == user ? "g yourself. You are":"g [src]. [p_they(TRUE)] [p_are()]"] now at [health]/[maxHealth] health.") //Cogscarabs get special interaction because they're drones and have innate self-heals/revives. -/mob/living/simple_animal/drone/cogscarab/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) +/mob/living/simple_animal/drone/cogscarab/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent) . = TRUE if(stat == DEAD) try_reactivate(user) //if we're at maximum health, prosel the turf under us @@ -374,17 +374,36 @@ proselytizer.repairing = null //Convert shards and gear bits directly to power -/obj/item/clockwork/alloy_shards/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) - return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "power_cost" = -POWER_STANDARD, "spawn_dir" = SOUTH) +/obj/item/clockwork/alloy_shards/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent, power_amount) + if(!power_amount) + power_amount = -POWER_STANDARD + if(proselytizer.can_use_power(power_amount)) + var/obj/effect/overlay/temp/ratvar/beam/itemconsume/B = new /obj/effect/overlay/temp/ratvar/beam/itemconsume(get_turf(src)) + B.pixel_x = pixel_x + B.pixel_y = pixel_y + if(!silent) //looper no looping + for(var/obj/item/clockwork/alloy_shards/S in get_turf(src)) //convert all other shards in the turf if we can + if(S == src) + continue //we want the shards to be proselytized after the main shard, thus this delay + addtimer(CALLBACK(proselytizer, /obj/item/clockwork/clockwork_proselytizer.proc/proselytize, S, user, TRUE), 0) + return list("operation_time" = 0, "new_obj_type" = null, "power_cost" = power_amount, "spawn_dir" = SOUTH) -/obj/item/clockwork/alloy_shards/medium/gear_bit/large/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) - return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.08), "spawn_dir" = SOUTH) +/obj/item/clockwork/alloy_shards/medium/gear_bit/large/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent, power_amount) + if(!power_amount) + power_amount = -(CLOCKCULT_POWER_UNIT*0.08) + return ..() -/obj/item/clockwork/alloy_shards/large/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) - return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.06), "spawn_dir" = SOUTH) +/obj/item/clockwork/alloy_shards/large/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent, power_amount) + if(!power_amount) + power_amount = -(CLOCKCULT_POWER_UNIT*0.06) + return ..() -/obj/item/clockwork/alloy_shards/medium/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) - return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.04), "spawn_dir" = SOUTH) +/obj/item/clockwork/alloy_shards/medium/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent, power_amount) + if(!power_amount) + power_amount = -(CLOCKCULT_POWER_UNIT*0.04) + return ..() -/obj/item/clockwork/alloy_shards/small/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer) - return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "power_cost" = -(CLOCKCULT_POWER_UNIT*0.02), "spawn_dir" = SOUTH) +/obj/item/clockwork/alloy_shards/small/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer, silent, power_amount) + if(!power_amount) + power_amount = -(CLOCKCULT_POWER_UNIT*0.02) + return ..() diff --git a/code/game/gamemodes/clock_cult/clock_items/clock_components.dm b/code/game/gamemodes/clock_cult/clock_items/clock_components.dm index 16cfef5d72..f64b361ac1 100644 --- a/code/game/gamemodes/clock_cult/clock_items/clock_components.dm +++ b/code/game/gamemodes/clock_cult/clock_items/clock_components.dm @@ -162,14 +162,15 @@ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF var/randomsinglesprite = FALSE var/randomspritemax = 2 + var/sprite_shift = 9 /obj/item/clockwork/alloy_shards/Initialize() . = ..() if(randomsinglesprite) replace_name_desc() icon_state = "[icon_state][rand(1, randomspritemax)]" - pixel_x = rand(-9, 9) - pixel_y = rand(-9, 9) + pixel_x = rand(-sprite_shift, sprite_shift) + pixel_y = rand(-sprite_shift, sprite_shift) /obj/item/clockwork/alloy_shards/proc/replace_name_desc() name = "replicant alloy shard" @@ -177,16 +178,21 @@ clockwork_desc = "A broken shard of replicant alloy. Can be proselytized for additional power." /obj/item/clockwork/alloy_shards/large + w_class = WEIGHT_CLASS_TINY randomsinglesprite = TRUE icon_state = "shard_large" + sprite_shift = 9 /obj/item/clockwork/alloy_shards/medium + w_class = WEIGHT_CLASS_TINY randomsinglesprite = TRUE icon_state = "shard_medium" + sprite_shift = 10 /obj/item/clockwork/alloy_shards/medium/gear_bit randomspritemax = 4 icon_state = "gear_bit" + sprite_shift = 12 /obj/item/clockwork/alloy_shards/medium/gear_bit/replace_name_desc() name = "gear bit" @@ -200,9 +206,11 @@ name = "complex gear bit" /obj/item/clockwork/alloy_shards/small + w_class = WEIGHT_CLASS_TINY randomsinglesprite = TRUE randomspritemax = 3 icon_state = "shard_small" + sprite_shift = 12 /obj/item/clockwork/alloy_shards/pinion_lock name = "pinion lock" diff --git a/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm b/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm index 60df56b6ef..1d34ea2814 100644 --- a/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm +++ b/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm @@ -178,21 +178,24 @@ return TRUE //A note here; return values are for if we CAN BE PUT ON A TABLE, not IF WE ARE SUCCESSFUL, unless no_table_check is TRUE -/obj/item/clockwork/clockwork_proselytizer/proc/proselytize(atom/target, mob/living/user, no_table_check) +/obj/item/clockwork/clockwork_proselytizer/proc/proselytize(atom/target, mob/living/user, silent, no_table_check) if(!target || !user) return FALSE if(repairing) - to_chat(user, "You are currently repairing [repairing] with [src]!") + if(!silent) + to_chat(user, "You are currently repairing [repairing] with [src]!") return FALSE if(recharging) - to_chat(user, "You are currently recharging [src] from the [recharging.sigil_name]!") + if(!silent) + to_chat(user, "You are currently recharging [src] from the [recharging.sigil_name]!") return FALSE - var/list/proselytize_values = target.proselytize_vals(user, src) //relevant values for proselytizing stuff, given as an associated list + var/list/proselytize_values = target.proselytize_vals(user, src, silent) //relevant values for proselytizing stuff, given as an associated list if(!islist(proselytize_values)) if(proselytize_values != TRUE) //if we get true, fail, but don't send a message for whatever reason if(!isturf(target)) //otherwise, if we didn't get TRUE and the original target wasn't a turf, try to proselytize the turf return proselytize(get_turf(target), user, no_table_check) - to_chat(user, "[target] cannot be proselytized!") + if(!silent) + to_chat(user, "[target] cannot be proselytized!") if(!no_table_check) return TRUE return FALSE @@ -207,19 +210,22 @@ var/target_type = target.type - if(!proselytize_checks(proselytize_values, target, target_type, user)) + if(!proselytize_checks(proselytize_values, target, target_type, user, silent)) return FALSE proselytize_values["operation_time"] *= speed_multiplier playsound(target, 'sound/machines/click.ogg', 50, 1) if(proselytize_values["operation_time"]) - user.visible_message("[user]'s [name] begins tearing apart [target]!", "You begin proselytizing [target]...") + if(!silent) + user.visible_message("[user]'s [name] begins tearing apart [target]!", "You begin proselytizing [target]...") if(!do_after(user, proselytize_values["operation_time"], target = target, extra_checks = CALLBACK(src, .proc/proselytize_checks, proselytize_values, target, target_type, user, TRUE))) return FALSE - user.visible_message("[user]'s [name] covers [target] in golden energy!", "You proselytize [target].") + if(!silent) + user.visible_message("[user]'s [name] covers [target] in golden energy!", "You proselytize [target].") else - user.visible_message("[user]'s [name] tears apart [target], covering it in golden energy!", "You proselytize [target].") + if(!silent) + user.visible_message("[user]'s [name] tears apart [target], covering it in golden energy!", "You proselytize [target].") playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) var/new_thing_type = proselytize_values["new_obj_type"] @@ -227,11 +233,12 @@ var/turf/T = target T.ChangeTurf(new_thing_type) else - if(proselytize_values["dir_in_new"]) - new new_thing_type(get_turf(target), proselytize_values["spawn_dir"]) //please verify that your new object actually wants to get a dir in New() - else - var/atom/A = new new_thing_type(get_turf(target)) - A.setDir(proselytize_values["spawn_dir"]) + if(new_thing_type) + if(proselytize_values["dir_in_new"]) + new new_thing_type(get_turf(target), proselytize_values["spawn_dir"]) //please verify that your new object actually wants to get a dir in New() + else + var/atom/A = new new_thing_type(get_turf(target)) + A.setDir(proselytize_values["spawn_dir"]) if(!proselytize_values["no_target_deletion"]) //for some cases where proselytize_vals() modifies the object but doesn't want it deleted qdel(target) modify_stored_power(-proselytize_values["power_cost"]) diff --git a/icons/obj/clockwork_objects.dmi b/icons/obj/clockwork_objects.dmi index 4b52f332052d9cf19f6d52af5a395350f198d2c1..a7c80ba53617e273bed8ec2be93e84956c9358dd 100644 GIT binary patch delta 3206 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 delta 3319 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 Date: Thu, 18 May 2017 16:37:30 -0500 Subject: [PATCH 14/52] Plastic explosives can no longer be detonated by EMPs --- code/game/objects/items/weapons/grenades/plastic.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/game/objects/items/weapons/grenades/plastic.dm b/code/game/objects/items/weapons/grenades/plastic.dm index 3118d731f2..21a71f371d 100644 --- a/code/game/objects/items/weapons/grenades/plastic.dm +++ b/code/game/objects/items/weapons/grenades/plastic.dm @@ -18,6 +18,10 @@ plastic_overlay = mutable_appearance(icon, "[item_state]2") ..() +/obj/item/weapon/grenade/plastic/Initialize(mapload) + . = ..() + SET_SECONDARY_FLAG(src, NO_EMP_WIRES) + /obj/item/weapon/grenade/plastic/Destroy() qdel(nadeassembly) nadeassembly = null From abb9676c313080cd40599b0993e4292394def085 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:34 -0500 Subject: [PATCH 15/52] [Cere] Atmospherics Revamp --- _maps/map_files/Cerestation/cerestation.dmm | 1614 ++++++++++++++----- 1 file changed, 1219 insertions(+), 395 deletions(-) diff --git a/_maps/map_files/Cerestation/cerestation.dmm b/_maps/map_files/Cerestation/cerestation.dmm index 0bcbf8da32..0df3264dce 100644 --- a/_maps/map_files/Cerestation/cerestation.dmm +++ b/_maps/map_files/Cerestation/cerestation.dmm @@ -1,5 +1,4 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -//Model dictionary trimmed on: 12-05-2017 18:47 (UTC) "aaa" = ( /turf/open/space, /area/space) @@ -46766,6 +46765,7 @@ }) "bCK" = ( /obj/effect/turf_decal/stripes/asteroid/line, +/obj/machinery/atmospherics/pipe/simple/orange/visible, /turf/open/floor/plating/astplate{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -46773,7 +46773,6 @@ name = "Central Asteroid Maintenance" }) "bCL" = ( -/obj/machinery/atmospherics/pipe/simple/orange/hidden, /obj/structure/rack, /turf/open/floor/plating/astplate{ baseturf = /turf/open/floor/plating/asteroid/airless @@ -47412,18 +47411,22 @@ density = 0; layer = 4 }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 6 + }, /turf/closed/wall/r_wall{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bDX" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 6 - }, /obj/machinery/door/airlock/maintenance{ name = "Atmospehrics Maintenance"; req_access_txt = "12;24" }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/orange/visible, /turf/open/floor/plating{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -47977,6 +47980,7 @@ /area/hydroponics) "bEY" = ( /obj/effect/turf_decal/delivery, +/obj/machinery/portable_atmospherics/canister/air, /turf/open/floor/plasteel/vault{ baseturf = /turf/open/floor/plating/asteroid/airless; dir = 5 @@ -48011,21 +48015,26 @@ icon_state = "tube1"; dir = 8 }, +/obj/item/weapon/twohanded/required/kirbyplants{ + tag = "icon-plant-21"; + icon_state = "plant-21" + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bFd" = ( /obj/structure/closet/firecloset/full, +/obj/machinery/atmospherics/pipe/simple/scrubbers/visible, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bFe" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/visible, /obj/effect/turf_decal/stripes/line{ dir = 1 }, +/obj/machinery/atmospherics/pipe/simple/orange/visible, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -48036,7 +48045,6 @@ /obj/item/stack/rods{ amount = 50 }, -/obj/machinery/atmospherics/pipe/simple/orange/visible, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -48063,6 +48071,11 @@ /obj/structure/table, /obj/item/weapon/grenade/chem_grenade/metalfoam, /obj/item/weapon/grenade/chem_grenade/metalfoam, +/obj/machinery/airalarm{ + frequency = 1439; + locked = 0; + pixel_y = 23 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -49042,6 +49055,10 @@ icon_state = "1-2"; pixel_y = 0 }, +/obj/machinery/atmospherics/components/unary/vent_pump{ + dir = 4; + on = 1 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -49051,6 +49068,10 @@ dir = 4 }, /obj/effect/landmark/start/atmospheric_technician, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + icon_state = "intact"; + dir = 8 + }, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (EAST)"; icon_state = "yellow"; @@ -49065,13 +49086,22 @@ id = "atmodesk" }, /obj/machinery/door/window/eastright{ + name = "Atmospherics Desk"; req_access_txt = "24" }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + icon_state = "intact"; + dir = 8 + }, /turf/open/floor/plasteel/yellow{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bGU" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + icon_state = "intact"; + dir = 8 + }, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (WEST)"; icon_state = "yellow"; @@ -49091,13 +49121,11 @@ icon_state = "1-2"; pixel_y = 0 }, -/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ - dir = 8 - }, /obj/structure/disposalpipe/segment{ dir = 4; icon_state = "pipe-c" }, +/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -50714,6 +50742,7 @@ icon_state = "camera"; network = list("SS13","CE") }, +/obj/machinery/portable_atmospherics/scrubber, /turf/open/floor/plasteel/vault{ baseturf = /turf/open/floor/plating/asteroid/airless; dir = 5 @@ -50744,16 +50773,19 @@ }, /area/atmos) "bJM" = ( -/obj/machinery/atmospherics/pipe/simple/orange/visible{ - dir = 5 +/obj/machinery/atmospherics/components/unary/vent_scrubber{ + dir = 4; + on = 1; + scrub_N2O = 0; + scrub_Toxins = 0 }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bJN" = ( -/obj/machinery/atmospherics/pipe/simple/orange/visible{ - dir = 10 +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless @@ -51249,31 +51281,30 @@ name = "Lounge" }) "bKF" = ( -/obj/structure/extinguisher_cabinet{ - pixel_x = -24 - }, -/turf/open/floor/plasteel/yellow/side{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/visible, +/obj/machinery/meter, +/turf/open/floor/plasteel/yellow/corner{ + dir = 8 }, /area/atmos) "bKG" = ( -/obj/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/visible{ +/obj/machinery/atmospherics/pipe/simple/orange/visible, +/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ + icon_state = "intact"; dir = 8 }, -/turf/open/floor/plasteel/yellow/side{ +/turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bKH" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ +/obj/effect/landmark/lightsout, +/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ + dir = 10; icon_state = "intact"; - dir = 10 + tag = "icon-intact (SOUTHEAST)" }, -/turf/open/floor/plasteel/yellow/corner{ - tag = "icon-yellowcorner (WEST)"; - icon_state = "yellowcorner"; - dir = 8; +/turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -51306,6 +51337,8 @@ icon_state = "camera"; network = list("SS13","CE") }, +/obj/structure/closet/secure_closet/atmospherics, +/obj/item/weapon/pickaxe/mini, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -51964,7 +51997,11 @@ }, /area/atmos) "bLV" = ( -/obj/machinery/pipedispenser/disposal/transit_tube, +/obj/machinery/door/window/eastright{ + dir = 1; + name = "Interior Pipe Access"; + req_access_txt = "24" + }, /turf/open/floor/plasteel/black{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -51975,16 +52012,14 @@ }, /area/atmos) "bLX" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, /obj/machinery/atmospherics/components/binary/pump{ - dir = 2; - name = "Waste To Filter"; - on = 1 + dir = 1; + name = "Mix To Engine"; + on = 0; + target_pressure = 101 }, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless +/turf/open/floor/plasteel/yellow/corner{ + dir = 8 }, /area/atmos) "bLY" = ( @@ -52015,6 +52050,8 @@ pixel_x = 30; pixel_y = 0 }, +/obj/structure/closet/secure_closet/atmospherics, +/obj/item/weapon/pickaxe/mini, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -53034,6 +53071,8 @@ dir = 4; icon_state = "tube1" }, +/obj/machinery/disposal/bin, +/obj/structure/disposalpipe/trunk, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -53546,23 +53585,8 @@ /turf/open/floor/plating, /area/atmos) "bOv" = ( -/obj/machinery/computer/atmos_control/tank{ - frequency = 1441; - input_tag = "n2_in"; - name = "Nitrogen Supply Control"; - output_tag = "n2_out"; - sensors = list("n2_sensor" = "Tank") - }, -/obj/machinery/atmospherics/pipe/simple/green/visible, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless - }, -/area/atmos) -"bOw" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - icon_state = "intact"; - dir = 5 - }, +/obj/machinery/atmospherics/pipe/simple/yellow/visible, +/obj/machinery/meter, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (WEST)"; icon_state = "yellow"; @@ -53570,17 +53594,31 @@ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) +"bOw" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible{ + dir = 5 + }, +/obj/machinery/meter, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) "bOx" = ( -/obj/machinery/atmospherics/pipe/manifold/yellow/visible, +/obj/machinery/atmospherics/pipe/simple/general/visible{ + dir = 10; + pixel_x = 0; + initialize_directions = 10 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bOy" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - dir = 4 - }, -/turf/open/floor/plasteel{ +/obj/machinery/atmospherics/pipe/simple/general/visible, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (WEST)"; + icon_state = "yellow"; + dir = 8; baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -53590,9 +53628,11 @@ d2 = 4; icon_state = "1-4" }, -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - dir = 4 +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" }, +/obj/structure/chair/stool/bar, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -53603,9 +53643,12 @@ d2 = 8; icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ +/obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/table, +/obj/item/weapon/storage/fancy/cigarettes, +/obj/item/weapon/lighter/greyscale, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -53616,14 +53659,15 @@ d2 = 8; icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - dir = 4 - }, /obj/item/device/radio/intercom{ broadcasting = 0; name = "Station Intercom (General)"; pixel_y = 20 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/chair/stool/bar, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -53634,13 +53678,12 @@ d2 = 8; icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - icon_state = "intact"; - dir = 10 - }, /obj/structure/sign/atmosplaque{ pixel_y = 32 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -53651,11 +53694,11 @@ d2 = 8; icon_state = "1-8" }, -/obj/machinery/atmospherics/pipe/simple/supply/visible, /obj/structure/disposalpipe/segment{ - dir = 1; + dir = 8; icon_state = "pipe-c" }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -54301,14 +54344,6 @@ }, /area/atmos) "bPC" = ( -/obj/machinery/atmospherics/pipe/simple/general/visible{ - dir = 4 - }, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless - }, -/area/atmos) -"bPD" = ( /obj/machinery/atmospherics/pipe/simple/green/visible, /obj/machinery/atmospherics/components/binary/pump{ dir = 4; @@ -54316,9 +54351,19 @@ on = 1 }, /obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/red, +/area/atmos) +"bPD" = ( +/obj/machinery/atmospherics/pipe/simple/cyan/visible{ dir = 4 }, -/turf/open/floor/plasteel/black{ +/obj/machinery/atmospherics/pipe/simple/yellow/visible, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (WEST)"; + icon_state = "yellow"; + dir = 8; baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -54382,7 +54427,9 @@ }, /area/atmos) "bPL" = ( -/obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 10 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -54973,9 +55020,9 @@ /area/atmos) "bQG" = ( /obj/machinery/atmospherics/pipe/simple/yellow/visible{ - icon_state = "intact"; - dir = 10 + dir = 4 }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (WEST)"; icon_state = "yellow"; @@ -54990,12 +55037,11 @@ }, /area/atmos) "bQI" = ( -/obj/structure/closet/secure_closet/atmospherics, -/obj/item/weapon/pickaxe/mini, -/turf/open/floor/plasteel/yellow/side{ - tag = "icon-yellow (EAST)"; - icon_state = "yellow"; - dir = 4; +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "North Canister to Waste" + }, +/turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -55008,13 +55054,11 @@ }, /area/atmos) "bQK" = ( -/obj/machinery/atmospherics/components/unary/portables_connector/visible{ - tag = "icon-connector_map (EAST)"; - icon_state = "connector_map"; - dir = 4 +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 1 }, -/obj/machinery/portable_atmospherics/canister, -/turf/open/floor/plasteel/delivery{ +/obj/machinery/meter, +/turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -55042,10 +55086,7 @@ /obj/structure/fireaxecabinet{ pixel_x = 32 }, -/obj/machinery/disposal/bin, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -55530,8 +55571,9 @@ }, /area/atmos) "bRI" = ( -/obj/machinery/atmospherics/pipe/manifold4w/yellow/visible, -/obj/machinery/meter, +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 1 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -55548,22 +55590,17 @@ /obj/machinery/light{ dir = 1 }, -/obj/machinery/atmospherics/components/unary/portables_connector/visible{ - tag = "icon-connector_map (EAST)"; - icon_state = "connector_map"; - dir = 4 - }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bRL" = ( -/obj/machinery/meter, -/obj/machinery/atmospherics/pipe/simple/cyan/visible{ - tag = "icon-intact (WEST)"; - icon_state = "intact"; - dir = 8 +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + tag = "icon-connector_map (EAST)"; + icon_state = "connector_map"; + dir = 4 }, +/obj/machinery/portable_atmospherics/pump, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -55574,6 +55611,7 @@ icon_state = "intact"; dir = 8 }, +/obj/machinery/meter, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (EAST)"; icon_state = "yellow"; @@ -55595,6 +55633,7 @@ /area/atmos) "bRO" = ( /obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (WEST)"; icon_state = "yellow"; @@ -55603,9 +55642,7 @@ }, /area/atmos) "bRP" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ - tag = "icon-intact (SOUTHEAST)"; - icon_state = "intact"; +/obj/machinery/atmospherics/pipe/simple/yellow/visible{ dir = 6 }, /turf/open/floor/plasteel{ @@ -55614,8 +55651,8 @@ /area/atmos) "bRQ" = ( /obj/machinery/atmospherics/components/binary/pump{ - dir = 8; - name = "Waste to Filter"; + dir = 4; + name = "Filter to Waste"; on = 1 }, /turf/open/floor/plasteel{ @@ -56039,19 +56076,16 @@ }, /area/atmos) "bSI" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - dir = 9 +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 }, -/turf/open/floor/plasteel{ +/turf/open/floor/plasteel/delivery{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bSJ" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - icon_state = "intact"; - dir = 5 - }, -/turf/open/floor/plasteel{ +/obj/machinery/pipedispenser/disposal, +/turf/open/floor/plasteel/yellow/corner{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -56064,19 +56098,18 @@ }, /area/atmos) "bSL" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - dir = 4 +/obj/machinery/atmospherics/components/binary/pump{ + dir = 8; + name = "Pure To Canisters"; + on = 0 }, -/obj/machinery/meter, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bSM" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 8; - name = "Mix To Canisters"; - on = 0 +/obj/machinery/atmospherics/pipe/simple/yellow/visible{ + dir = 4 }, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (EAST)"; @@ -56099,11 +56132,8 @@ }, /area/atmos) "bSO" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 2; - name = "Mix to Filter"; - on = 1 - }, +/obj/machinery/atmospherics/pipe/manifold/yellow/visible, +/obj/machinery/meter, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -56503,11 +56533,7 @@ }, /area/atmos) "bTA" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - tag = "icon-intact (NORTHEAST)"; - icon_state = "intact"; - dir = 5 - }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (WEST)"; icon_state = "yellow"; @@ -56747,13 +56773,20 @@ /area/atmos) "bTZ" = ( /obj/effect/landmark/event_spawn, +/obj/machinery/atmospherics/components/unary/vent_scrubber{ + dir = 4; + on = 1; + scrub_N2O = 0; + scrub_Toxins = 0 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bUa" = ( -/obj/machinery/atmospherics/pipe/manifold/yellow/visible{ - dir = 8 +/obj/machinery/atmospherics/pipe/simple/yellow/visible, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 }, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (EAST)"; @@ -57157,16 +57190,16 @@ }, /area/atmos) "bUL" = ( -/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/machinery/atmospherics/pipe/simple/yellow/visible, /obj/machinery/atmospherics/pipe/simple/cyan/visible{ tag = "icon-intact (WEST)"; icon_state = "intact"; dir = 8 }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/turf/open/floor/plasteel/yellow{ +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (WEST)"; + icon_state = "yellow"; + dir = 8; baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -57185,20 +57218,16 @@ }, /area/atmos) "bUN" = ( -/obj/machinery/atmospherics/pipe/simple/cyan/visible{ - tag = "icon-intact (NORTHWEST)"; - icon_state = "intact"; - dir = 9 +/obj/machinery/atmospherics/pipe/simple/general/visible{ + dir = 5 }, -/turf/open/floor/plasteel/yellow/side{ +/turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bUO" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - dir = 6 - }, -/turf/open/floor/plasteel/yellow/side{ +/obj/machinery/atmospherics/pipe/manifold/general/visible, +/turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -57262,6 +57291,10 @@ dir = 4; icon_state = "tube1" }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + icon_state = "intact"; + dir = 4 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -57451,32 +57484,22 @@ }, /area/atmos) "bVt" = ( -/obj/machinery/computer/atmos_control/tank{ - frequency = 1441; - input_tag = "air_in"; - name = "Mixed Air Supply Control"; - output_tag = "air_out"; - sensors = list("air_sensor" = "Tank") +/obj/machinery/atmospherics/pipe/manifold/yellow/visible{ + dir = 8 }, -/obj/machinery/atmospherics/pipe/simple/green/visible{ - tag = "icon-intact (NORTHEAST)"; - icon_state = "intact"; - dir = 5 - }, -/turf/open/floor/plasteel/vault{ - baseturf = /turf/open/floor/plating/asteroid/airless; - dir = 5 +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (SOUTHWEST)"; + icon_state = "yellow"; + dir = 10; + baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bVu" = ( -/obj/machinery/atmospherics/pipe/simple/green/visible{ - tag = "icon-intact (WEST)"; +/obj/machinery/atmospherics/pipe/simple/yellow/visible{ icon_state = "intact"; dir = 8 }, -/obj/machinery/atmospherics/pipe/simple/yellow/visible, -/obj/structure/window/reinforced, -/turf/open/floor/plasteel/yellow{ +/turf/open/floor/plasteel/yellow/side{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -57532,14 +57555,11 @@ }, /area/atmos) "bVy" = ( -/obj/machinery/atmospherics/pipe/simple/green/visible{ +/obj/machinery/atmospherics/pipe/simple/yellow/visible{ dir = 4 }, -/obj/structure/window/reinforced{ - dir = 1; - layer = 2.9 - }, -/turf/open/floor/plasteel/black{ +/obj/machinery/atmospherics/pipe/simple/general/visible, +/turf/open/floor/plasteel/yellow/side{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) @@ -57939,75 +57959,129 @@ }, /area/atmos) "bWe" = ( -/obj/machinery/atmospherics/pipe/manifold/cyan/visible{ - dir = 1; - initialize_directions = 11 +/obj/machinery/atmospherics/pipe/simple/cyan/visible{ + dir = 10; + initialize_directions = 10 }, /turf/open/floor/plasteel/black{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bWf" = ( -/obj/machinery/atmospherics/components/binary/pump{ - dir = 4; - name = "Air to Pure" +/obj/machinery/atmospherics/pipe/simple/green/visible{ + tag = "icon-intact (NORTHEAST)"; + icon_state = "intact"; + dir = 5 }, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/structure/window/reinforced{ + dir = 8 }, +/obj/structure/window/reinforced, +/obj/structure/table, +/obj/item/weapon/paper_bin{ + pixel_x = -3; + pixel_y = 7 + }, +/turf/open/floor/plasteel/barber, /area/atmos) "bWg" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ +/obj/machinery/atmospherics/pipe/simple/green/visible{ tag = "icon-intact (WEST)"; icon_state = "intact"; dir = 8 }, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/structure/window/reinforced, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 1; + name = "Air to Pure"; + on = 0 }, +/turf/open/floor/plasteel/barber, /area/atmos) "bWh" = ( -/obj/machinery/atmospherics/pipe/manifold/yellow/visible, -/obj/machinery/meter, +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window/eastright{ + dir = 1; + name = "Interior Pipe Access"; + req_access_txt = "24" + }, /turf/open/floor/plasteel/black{ baseturf = /turf/open/floor/plating/asteroid/airless }, /area/atmos) "bWi" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - tag = "icon-intact (WEST)"; - icon_state = "intact"; - dir = 8 +/obj/machinery/atmospherics/components/trinary/filter{ + dir = 4; + filter_type = "co2"; + name = "co2 filter"; + on = 1 }, -/obj/machinery/atmospherics/pipe/simple/green/visible, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/vault{ + initial_gas_mix = "n2=100;TEMP=80"; + temperature = 80 }, /area/atmos) "bWj" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible{ - tag = "icon-intact (NORTHWEST)"; - icon_state = "intact"; - dir = 9 +/obj/machinery/computer/atmos_control/tank{ + frequency = 1441; + input_tag = "co2_in"; + name = "Carbon Dioxide Supply Control"; + output_tag = "co2_out"; + sensors = list("co2_sensor" = "Tank") }, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/vault{ + dir = 5; + initial_gas_mix = "n2=500;TEMP=80"; + temperature = 80 }, /area/atmos) "bWk" = ( -/obj/machinery/atmospherics/pipe/simple/yellow/visible, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 1; + name = "CO2 to Pure"; + on = 0 + }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/vault{ + initial_gas_mix = "n2=100;TEMP=80"; + temperature = 80 }, /area/atmos) "bWl" = ( -/obj/machinery/atmospherics/pipe/simple/green/visible, -/turf/open/floor/plasteel/black{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/machinery/atmospherics/components/trinary/filter{ + dir = 4; + filter_type = "plasma"; + name = "plasma filter"; + on = 1 }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/purple, /area/atmos) "bWm" = ( -/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 2; + name = "Unfiltered to Mix"; + on = 1 + }, /turf/open/floor/plasteel/yellow/side{ tag = "icon-yellow (WEST)"; icon_state = "yellow"; @@ -58019,6 +58093,11 @@ /obj/structure/extinguisher_cabinet{ pixel_x = 24 }, +/obj/machinery/atmospherics/pipe/simple/cyan/visible{ + tag = "icon-intact (NORTHWEST)"; + icon_state = "intact"; + dir = 9 + }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -58219,7 +58298,6 @@ /obj/machinery/light{ dir = 8 }, -/obj/machinery/pipedispenser/disposal, /turf/open/floor/plasteel/black{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -58251,15 +58329,13 @@ }, /area/atmos) "bWJ" = ( -/obj/machinery/atmospherics/pipe/simple/cyan/visible{ - dir = 4 - }, /obj/machinery/camera{ c_tag = "Atmoshperic Tanks 1"; dir = 1; icon_state = "camera"; network = list("SS13","CE") }, +/obj/machinery/atmospherics/pipe/manifold/cyan/visible, /turf/open/floor/plasteel/black{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -79479,9 +79555,9 @@ }, /area/crew_quarters/fitness) "cHs" = ( -/obj/effect/landmark/start/atmospheric_technician, -/turf/open/floor/plasteel{ - baseturf = /turf/open/floor/plating/asteroid/airless +/obj/machinery/pipedispenser/disposal/transit_tube, +/turf/open/floor/plasteel/yellow/corner{ + dir = 4 }, /area/atmos) "cHt" = ( @@ -91948,6 +92024,754 @@ /area/maintenance/port{ name = "Port Asteroid Maintenance" }) +"deK" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + icon_state = "intact"; + dir = 8 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"deL" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + icon_state = "intact"; + dir = 8 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"deM" = ( +/obj/structure/extinguisher_cabinet{ + pixel_x = -24 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"deN" = ( +/obj/machinery/atmospherics/pipe/simple/orange/visible, +/obj/machinery/meter, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"deO" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + pixel_y = 0 + }, +/obj/structure/closet/secure_closet/atmospherics, +/obj/item/weapon/pickaxe/mini, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"deP" = ( +/obj/machinery/atmospherics/components/unary/outlet_injector/on{ + dir = 4; + frequency = 1441; + id = "n2_in" + }, +/obj/structure/lattice/catwalk, +/turf/open/space/basic, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deQ" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/closed/mineral, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deR" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/closed/mineral, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deS" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/closed/mineral, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deT" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/closed/mineral, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deU" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/closed/mineral/random/labormineral, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deV" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/closed/wall{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deW" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plating{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deX" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/simple/orange/hidden, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plating{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/maintenance/maintcentral{ + name = "Central Asteroid Maintenance" + }) +"deY" = ( +/obj/effect/turf_decal/delivery, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/portable_atmospherics/scrubber/huge, +/turf/open/floor/plasteel/vault{ + baseturf = /turf/open/floor/plating/asteroid/airless; + dir = 5 + }, +/area/atmos) +"deZ" = ( +/obj/machinery/portable_atmospherics/canister/carbon_dioxide, +/obj/effect/turf_decal/delivery, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel/vault{ + baseturf = /turf/open/floor/plating/asteroid/airless; + dir = 5 + }, +/area/atmos) +"dfa" = ( +/obj/machinery/portable_atmospherics/canister/nitrous_oxide, +/obj/effect/turf_decal/delivery, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel/vault{ + baseturf = /turf/open/floor/plating/asteroid/airless; + dir = 5 + }, +/area/atmos) +"dfb" = ( +/obj/machinery/portable_atmospherics/canister/nitrogen, +/obj/effect/turf_decal/delivery, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel/vault{ + baseturf = /turf/open/floor/plating/asteroid/airless; + dir = 5 + }, +/area/atmos) +"dfc" = ( +/obj/machinery/atmospherics/components/binary/valve/digital{ + dir = 4; + name = "Waste Release" + }, +/turf/open/floor/plasteel/yellow/side{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfd" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/visible, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (SOUTHWEST)"; + icon_state = "yellow"; + dir = 10; + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfe" = ( +/obj/machinery/atmospherics/components/trinary/filter{ + dir = 2; + filter_type = "n2"; + name = "nitogren filter"; + on = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/red, +/area/atmos) +"dff" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfg" = ( +/obj/structure/sign/nosmoking_2, +/turf/closed/wall/r_wall{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfh" = ( +/obj/machinery/computer/atmos_control/tank{ + frequency = 1441; + input_tag = "n2_in"; + name = "Nitrogen Supply Control"; + output_tag = "n2_out"; + sensors = list("n2_sensor" = "Tank") + }, +/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/red, +/area/atmos) +"dfi" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfj" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 5 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfk" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 4; + name = "N2 to Pure" + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/machinery/door/window/eastright{ + name = "Interior Pipe Access"; + req_access_txt = "24" + }, +/turf/open/floor/plasteel/black{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfl" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfm" = ( +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ + dir = 1 + }, +/turf/closed/wall{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/crew_quarters/chief) +"dfn" = ( +/obj/machinery/atmospherics/components/trinary/filter{ + dir = 2; + filter_type = "o2"; + name = "oxygen filter"; + on = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/blue, +/area/atmos) +"dfo" = ( +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 8 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfp" = ( +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 1 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfq" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible{ + dir = 10 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfr" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfs" = ( +/obj/machinery/computer/atmos_control/tank{ + frequency = 1441; + input_tag = "o2_in"; + name = "Oxygen Supply Control"; + output_tag = "o2_out"; + sensors = list("o2_sensor" = "Tank") + }, +/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/blue, +/area/atmos) +"dft" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (EAST)"; + icon_state = "yellow"; + dir = 4 + }, +/area/atmos) +"dfu" = ( +/obj/machinery/pipedispenser, +/obj/machinery/light, +/turf/open/floor/plasteel/yellow/corner{ + tag = "icon-yellowcorner (WEST)"; + icon_state = "yellowcorner"; + dir = 8 + }, +/area/atmos) +"dfv" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 + }, +/turf/open/floor/plasteel/delivery{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfw" = ( +/obj/machinery/atmospherics/components/unary/portables_connector/visible{ + dir = 1 + }, +/turf/open/floor/plasteel/delivery{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfx" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible{ + icon_state = "intact"; + dir = 6 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfy" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 4; + name = "O2 to Airmix"; + on = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/blue, +/area/atmos) +"dfz" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible, +/obj/machinery/camera{ + c_tag = "Atmospherics Distro"; + dir = 9; + icon_state = "camera"; + network = list("SS13","CE") + }, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (EAST)"; + icon_state = "yellow"; + dir = 4 + }, +/area/atmos) +"dfA" = ( +/obj/structure/sign/poster/official/safety_internals, +/turf/closed/wall, +/area/atmos) +"dfB" = ( +/obj/machinery/camera{ + c_tag = "Atmospherics South"; + dir = 4; + icon_state = "camera"; + network = list("SS13","CE") + }, +/obj/machinery/atmospherics/pipe/manifold/general/visible{ + dir = 8 + }, +/obj/machinery/meter, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (WEST)"; + icon_state = "yellow"; + dir = 8; + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfC" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible{ + icon_state = "intact"; + dir = 4 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfD" = ( +/obj/machinery/atmospherics/pipe/manifold/general/visible, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfE" = ( +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 4; + initialize_directions = 11 + }, +/turf/closed/wall/r_wall{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfF" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 4; + name = "O2 to Pure" + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/machinery/door/window/eastright{ + name = "Interior Pipe Access"; + req_access_txt = "24" + }, +/turf/open/floor/plasteel/black{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfG" = ( +/obj/machinery/atmospherics/components/binary/pump{ + dir = 1; + name = "South Canister To Waste"; + on = 0; + target_pressure = 101 + }, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (EAST)"; + icon_state = "yellow"; + dir = 4 + }, +/area/atmos) +"dfH" = ( +/obj/machinery/atmospherics/components/unary/thermomachine/freezer, +/obj/machinery/light{ + dir = 1 + }, +/turf/open/floor/plasteel/yellow/corner{ + dir = 1 + }, +/area/atmos) +"dfI" = ( +/obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/components/unary/portables_connector/visible, +/turf/open/floor/plasteel/delivery{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfJ" = ( +/obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/components/unary/portables_connector/visible, +/turf/open/floor/plasteel/delivery{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfK" = ( +/obj/machinery/portable_atmospherics/canister, +/obj/machinery/atmospherics/components/unary/portables_connector/visible, +/turf/open/floor/plasteel/delivery{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfL" = ( +/obj/effect/landmark/start/atmospheric_technician, +/obj/machinery/atmospherics/pipe/simple/general/visible, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (WEST)"; + icon_state = "yellow"; + dir = 8; + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfM" = ( +/obj/machinery/atmospherics/pipe/simple/cyan/visible, +/obj/structure/grille, +/obj/structure/window/reinforced/fulltile, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plating{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfN" = ( +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (WEST)"; + icon_state = "yellow"; + dir = 8; + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfO" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber{ + dir = 8; + on = 1; + scrub_Toxins = 0 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfP" = ( +/obj/machinery/status_display{ + density = 0; + layer = 4 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/closed/wall/r_wall{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfQ" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible, +/obj/machinery/atmospherics/pipe/simple/cyan/visible{ + tag = "icon-intact (WEST)"; + icon_state = "intact"; + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/open/floor/plasteel/barber, +/area/atmos) +"dfR" = ( +/obj/machinery/atmospherics/pipe/manifold4w/general/visible, +/obj/machinery/meter, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfS" = ( +/obj/machinery/atmospherics/pipe/simple/general/visible{ + dir = 9 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfT" = ( +/obj/machinery/atmospherics/pipe/manifold/yellow/visible{ + dir = 8 + }, +/obj/machinery/meter, +/turf/open/floor/plasteel/yellow/side{ + tag = "icon-yellow (EAST)"; + icon_state = "yellow"; + dir = 4; + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfU" = ( +/obj/machinery/atmospherics/components/unary/vent_pump{ + dir = 4; + on = 1 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfV" = ( +/obj/machinery/computer/atmos_control/tank{ + frequency = 1441; + input_tag = "air_in"; + name = "Mixed Air Supply Control"; + output_tag = "air_out"; + sensors = list("air_sensor" = "Tank") + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/green/visible, +/turf/open/floor/plasteel/barber, +/area/atmos) +"dfW" = ( +/obj/machinery/atmospherics/pipe/simple/cyan/visible, +/obj/machinery/airalarm{ + dir = 8; + icon_state = "alarm0"; + pixel_x = 24 + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfX" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window/eastright{ + dir = 1; + name = "Interior Pipe Access"; + req_access_txt = "24" + }, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 1; + name = "Air to South Canister"; + on = 0 + }, +/turf/open/floor/plasteel/black{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dfY" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/machinery/computer/atmos_control/tank{ + frequency = 1441; + input_tag = "tox_in"; + name = "Plasma Supply Control"; + output_tag = "tox_out"; + sensors = list("tox_sensor" = "Tank") + }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/purple, +/area/atmos) +"dfZ" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 1; + name = "Plasma to Pure"; + on = 0 + }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/purple, +/area/atmos) +"dga" = ( +/obj/machinery/atmospherics/components/trinary/filter{ + dir = 4; + filter_type = "n2"; + name = "nitogren filter"; + on = 1 + }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/escape{ + dir = 10 + }, +/area/atmos) +"dgb" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/machinery/computer/atmos_control/tank{ + frequency = 1441; + input_tag = "n2o_in"; + name = "Nitrous Oxide Supply Control"; + output_tag = "n2o_out"; + sensors = list("n2o_sensor" = "Tank") + }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/escape, +/area/atmos) +"dgc" = ( +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/obj/machinery/atmospherics/components/binary/pump{ + dir = 1; + name = "N2O to Pure"; + on = 0 + }, +/obj/structure/window/reinforced, +/turf/open/floor/plasteel/escape{ + dir = 6 + }, +/area/atmos) +"dgd" = ( +/obj/machinery/atmospherics/pipe/simple/cyan/visible, +/obj/structure/grille, +/obj/structure/window/reinforced/fulltile, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/green/visible{ + dir = 4 + }, +/turf/open/floor/plating{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) +"dge" = ( +/obj/machinery/atmospherics/pipe/manifold/cyan/visible, +/turf/open/floor/plasteel/black{ + baseturf = /turf/open/floor/plating/asteroid/airless + }, +/area/atmos) (1,1,1) = {" aaa @@ -107243,7 +108067,7 @@ btk btk btk ddI -cJW +ddI btk btk btk @@ -108016,7 +108840,7 @@ bxl ddJ ddX bte -bBF +dem cHi bDU bte @@ -108273,7 +109097,7 @@ bte byn bzm bte -bBF +dem bCC bDO bte @@ -109041,10 +109865,10 @@ bzr ddn bwc ddB -ddL +ddv dea bAq -dep +ddo dey deE bzr @@ -109297,12 +110121,12 @@ brI btj ddo bwc -ddC -ddM -deb +ddB +ddv +dea dej -deq -dez +ddo +dey cUI bzr bGC @@ -109559,7 +110383,7 @@ ddN bzn bAr buB -bzo +ddY bDP bzr bGD @@ -109809,14 +110633,14 @@ boR aYW brI btj -ddp +ddo ddv -ddE -ddO -dec +ddB +ddv +dea bwc -der -deA +ddo +dey bDQ bzr bGE @@ -110066,14 +110890,14 @@ boR aYW brJ bzr -ddq -ddw -ddF -ddP -ded +ddn +ddv +ddB +ddv +dea bwc -des -deB +ddo +dey bDR bzr bGF @@ -110327,7 +111151,7 @@ byq bwd bxn ddQ -bzo +ddY byp det bzm @@ -110581,7 +111405,7 @@ bqm brL bzr ddr -ddx +ddr bxo ddR dee @@ -111098,10 +111922,10 @@ buC ddy bxq ddT -def +ddV bAu -deu -bCF +ddG +ddV bDT bzr bGJ @@ -111359,7 +112183,7 @@ bzp dek bBG bwg -deG +ddG bzr bGK bIk @@ -111612,11 +112436,11 @@ buE ddz ddG ddV -deg +ddG del -dev +ddV deC -bCF +ddV bzr bQB bGL @@ -111875,7 +112699,7 @@ btk btk btk bEX -deI +dei bTq aXZ bCG @@ -116504,9 +117328,9 @@ aaa aaa aaa aaa -aaa -aaa -aaa +deP +abC +abC abC aaa aaa @@ -116761,7 +117585,7 @@ aaa aaa aaa aXS -aXS +deQ aXS aXE aYh @@ -117018,7 +117842,7 @@ aaa aXE aXE aXS -aXS +deR aXS aXS aXS @@ -117275,7 +118099,7 @@ aXS aXS aXS aXS -aXS +deS aXS aXS aXS @@ -117532,7 +118356,7 @@ aYg aYg aXS aXS -aXS +deT aXS aXS aXS @@ -117789,7 +118613,7 @@ bzs bzs bzs bzs -bzs +deU bxK bxK bwA @@ -118046,7 +118870,7 @@ cHu bwA bzs bwA -bwA +deV cHu bwA bvh @@ -118303,7 +119127,7 @@ cQI bwA cHu cHu -bYq +deW cQe cQC cRq @@ -118560,7 +119384,7 @@ cUT cUU cUV cUW -cUX +deX cUY cUY cUY @@ -118817,7 +119641,7 @@ bDV bDV bDV bDV -bDV +bDZ bDV bxK cHu @@ -119074,7 +119898,7 @@ bEY bEY bIm bJI -bEY +deY bLU bLU bLU @@ -119331,7 +120155,7 @@ bEZ bGM bIn bJJ -bJJ +deZ bLU bNt bOr @@ -119588,7 +120412,7 @@ bFa bFa bIn bJK -bJK +dfa bLU bNt bOs @@ -119845,7 +120669,7 @@ bFb bFb bIn bJL -bJL +dfb bLU bNu bOt @@ -120102,7 +120926,7 @@ bDV bDV bIo bDV -bDV +bDZ bLU bNv bOu @@ -120352,14 +121176,14 @@ aXS bxK bwA bxM -bBJ +cQB bxK bDV bFc bGN bGO -bGO -bDV +deM +dfc bLV bNw bLW @@ -120609,25 +121433,25 @@ aXS bxK cHu bwy -cQB +cUQ bCJ bDW bFd -bGO -bGO -bGO +bGP +bGP +bGP bKF -bLW -bNw -bLW +dfd +dfe +dfh bPC -bPC -bNw -bLW -bPC -bPC -bUK -bLW +dfk +dfn +dfs +dfy +dfF +dfQ +dfV bWf bUK bDV @@ -120866,27 +121690,27 @@ aXS bxK bxK cQu -cUQ +cUR bCK bDX bFe -bGP -bGP -bGP +bGQ +bGQ +deN bKG bLX -bNx +bNy bOv bPD -bQF -bRH -bSH -bTt -bTX +bTY +bNy +bNy +bTu +bTY bUL bVt bWg -bUK +dge bDV cHx cHz @@ -121123,24 +121947,24 @@ aYK aYK aYK bAB -cUR +bBJ bCL -bDY +bDZ bFf -bGQ -bGQ +bGO +bGO bJM bKH -bLY -bNy +bQM +dff bOw -bPE -bQG -bNy -bNy -bTu -bTY -bUM +bPF +bQH +bQH +bQH +bTv +bQH +bVK bVu bWh bUK @@ -121387,18 +122211,18 @@ bFg bGR bIp bJN -bKI -bGQ -bNz +bGO +bGO +bGO bOx -bPF -bQH -bQH -bQH -bTv -bQH +dfi +dfl +dfo +dft +dfz +dfG bUN -bVv +bVu bWi bWG bXg @@ -121641,21 +122465,21 @@ bBJ bwA bDZ bFh -bGO +deK bIq -bGO +bJN bKJ bGO -bNA -bOy -bPG -bQI -bQI -bQI -bPG bGO +bGO +bNA +bGO +bQI +dfu +bPH +dfH bUO -bVw +bVu bWj bWH bOu @@ -121898,21 +122722,21 @@ bBK cHu bDZ bFi -bGO +deL bIr -bGR +deO bKK bLZ bNB bOz -bPH -bQJ -bQJ -bQJ -bQO bGO +bGO +dfp +dfv +bQJ +dfI +bUO bUP -bVx bWk bWI bXh @@ -122157,20 +122981,20 @@ bDZ bFj bGS bIs +bDZ bDV bDV -bDV -bDV +dfg bOA -bPI -bQK -bQK -bQK -bTw bGO -bUQ +bGO +bQK +dfw +bQJ +dfJ +dfR bVy -bLW +dfX bWJ bLU bLU @@ -122414,19 +123238,19 @@ bEa bFk bGT bIt -bJO +bJP bKL bMa bDV bOB bGO -bQL +bGO bRI bSI -bGO -bGO +bQJ +dfK +bUO bUQ -bVz bWl bWG bXg @@ -122676,15 +123500,15 @@ bKM bMb bDV bOC -bPJ -bQM -bRJ -bSJ bGO +bGO +bQI +bSJ +dfA cHs +bQI bUQ -bVA -bLW +dfY bWH bOu bXD @@ -122933,15 +123757,15 @@ bKN bMc bNC bOD -bPK -bGO +dfj bGO +dfq bOy -bGO -bGO +dfB +dfL +dfS bUP -bVB -bWk +dfZ bWI bXh bXE @@ -123189,16 +124013,16 @@ bDZ bKM bMb bDV -bOE +bGO bPL bQN +dfr +bPK +dfC bGO -bSK -bSJ bGO bUQ -bVy -bLW +bWh bWK bLU bLU @@ -123450,12 +124274,12 @@ bOF bPM bQO bRK -bOy -bTx +dfx +dfD bTZ +bGO bUQ -bVC -bWl +dga bWG bXg bXF @@ -123707,12 +124531,12 @@ cRb cRb bQP bRL +bTx bSL -bSL +bJN bGO bUQ -bVD -bLW +dgb bWH bOu bXG @@ -123967,10 +124791,10 @@ bRM bSM bTy bUa +dfT bUR -bVE -bWk -bWL +dgc +bWI bXh bXH bYl @@ -124223,10 +125047,10 @@ bQQ bRN bSN bTz +dfM bUb bTz -bVF -bTz +dgd bWM bLU bLU @@ -124476,13 +125300,13 @@ bMg cRk cRs bPP -bQQ +dfm bRO bQG bTA +dfN bUc bUS -bVG bWm bWN bXg @@ -124736,11 +125560,11 @@ cRA cWQ bRP bSO -bOx +bGO +dfO bUd bUT bVH -bGO bWO bOu bXJ @@ -124992,12 +125816,12 @@ cRu cRB cWR bRQ -bGO bQL +bPJ bUe bPJ -bVI bPJ +bVI bWP bXh bXK @@ -125252,9 +126076,9 @@ bRR bSP bTB bUf +dfU bGO bVJ -bQH bWQ bLU bLU @@ -125510,7 +126334,7 @@ bSQ bTC bUg bUU -bVK +dfW bWn bWR bDV @@ -125764,9 +126588,9 @@ cRc bQS bRT bSR +dfE +dfP bTD -bDW -bDV bDV bDV bDV From bcf7ff1ddbac5e4f0eba643ba75c88a9587d8511 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:38 -0500 Subject: [PATCH 16/52] [Delta] Adds freon tank to engineering secure storage --- _maps/map_files/Deltastation/DeltaStation2.dmm | 1 + 1 file changed, 1 insertion(+) diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index c157cdc780..3c7218915c 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -63624,6 +63624,7 @@ dir = 8 }, /obj/effect/turf_decal/bot, +/obj/machinery/portable_atmospherics/canister/freon, /turf/open/floor/plasteel, /area/engine/engineering) "cqt" = ( From ef164ada8687b57efbc8c41c4db93598a831b852 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:42 -0500 Subject: [PATCH 17/52] fixes ratvar shuttles --- .../clock_structures/ratvar_the_clockwork_justicar.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm index b82e9a9c85..25f151def5 100644 --- a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm +++ b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm @@ -28,7 +28,7 @@ var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert") var/area/A = get_area(src) notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [A.name] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay) - INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency..proc/request, null, 0, 0) + INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 0, null, FALSE, 0) /obj/structure/destructible/clockwork/massive/ratvar/Destroy() GLOB.ratvar_awakens-- From c6a9bdb4b24f95a3a895713ee66662e6a587a868 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:50 -0500 Subject: [PATCH 18/52] Fixes clicking things under windows/doors. --- code/__DEFINES/flags.dm | 1 + code/_onclick/click.dm | 19 +++++++++++++++++++ code/game/machinery/doors/door.dm | 1 + code/game/objects/structures/window.dm | 12 ++++++------ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index b0742ae3ab..a5e69bdc7b 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define BLOCK_GAS_SMOKE_EFFECT 4096 // blocks the effect that chemical clouds would have on a mob --glasses, mask and helmets ONLY! #define THICKMATERIAL 8192 //prevents syringes, parapens and hypos if the external suit or helmet (if targeting head) has this flag. Example: space suits, biosuit, bombsuits, thick suits that cover your body. #define DROPDEL 16384 // When dropped, it calls qdel on itself +#define PREVENT_CLICK_UNDER 32768 //Prevent clicking things below it on the same turf eg. doors/ fulltile windows /* Secondary atom flags, access using the SECONDARY_FLAG macros */ diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 07dcfaee28..a0fc60a2fc 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -92,6 +92,9 @@ if(next_move > world.time) // in the year 2000... return + if(A.IsObscured()) + return + if(istype(loc,/obj/mecha)) var/obj/mecha/M = loc return M.click_action(A,src,params) @@ -141,6 +144,22 @@ else RangedAttack(A,params) +//Is the atom obscured by a PREVENT_CLICK_UNDER object above it +/atom/proc/IsObscured() + if(!isturf(loc)) //This only makes sense for things directly on turfs for now + return FALSE + var/turf/T = loc + for(var/atom/movable/AM in T) + if(AM.flags & PREVENT_CLICK_UNDER && AM.density && AM.layer > layer) + return TRUE + return FALSE + +/turf/IsObscured() + for(var/atom/movable/AM in src) + if(AM.flags & PREVENT_CLICK_UNDER && AM.density) + return TRUE + return FALSE + /atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE) if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 4e076e3338..b71aeaca8d 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -12,6 +12,7 @@ max_integrity = 350 armor = list(melee = 30, bullet = 30, laser = 20, energy = 20, bomb = 10, bio = 100, rad = 100, fire = 80, acid = 70) CanAtmosPass = ATMOS_PASS_DENSITY + flags = PREVENT_CLICK_UNDER var/secondsElectrified = 0 var/shockedby = list() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index d5027a0c75..f594c4b513 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -440,7 +440,7 @@ dir = FULLTILE_WINDOW_DIR max_integrity = 50 fulltile = 1 - flags = NONE + flags = PREVENT_CLICK_UNDER smooth = SMOOTH_TRUE canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile,/obj/structure/window/reinforced/highpressure/fulltile, /obj/structure/window/reinforced/tinted/fulltile) glass_amount = 2 @@ -454,7 +454,7 @@ dir = FULLTILE_WINDOW_DIR max_integrity = 100 fulltile = 1 - flags = NONE + flags = PREVENT_CLICK_UNDER smooth = SMOOTH_TRUE canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile,/obj/structure/window/reinforced/highpressure/fulltile, /obj/structure/window/reinforced/tinted/fulltile) @@ -467,7 +467,7 @@ dir = FULLTILE_WINDOW_DIR max_integrity = 1000 fulltile = 1 - flags = NONE + flags = PREVENT_CLICK_UNDER smooth = SMOOTH_TRUE canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile,/obj/structure/window/reinforced/highpressure/fulltile, /obj/structure/window/reinforced/tinted/fulltile) level = 3 @@ -481,7 +481,7 @@ icon_state = "tinted_window" dir = FULLTILE_WINDOW_DIR fulltile = 1 - flags = NONE + flags = PREVENT_CLICK_UNDER smooth = SMOOTH_TRUE canSmoothWith = list(/obj/structure/window/fulltile, /obj/structure/window/reinforced/fulltile,/obj/structure/window/reinforced/highpressure/fulltile, /obj/structure/window/reinforced/tinted/fulltile/) level = 3 @@ -504,7 +504,7 @@ max_integrity = 100 wtype = "shuttle" fulltile = 1 - flags = NONE + flags = PREVENT_CLICK_UNDER reinf = 1 heat_resistance = 1600 armor = list(melee = 50, bullet = 0, laser = 0, energy = 0, bomb = 25, bio = 100, rad = 100, fire = 80, acid = 100) @@ -583,7 +583,7 @@ smooth = SMOOTH_TRUE canSmoothWith = null fulltile = 1 - flags = NONE + flags = PREVENT_CLICK_UNDER dir = FULLTILE_WINDOW_DIR max_integrity = 120 level = 3 From fa1b651799a9d44864cfc9e5b05b1bc661248935 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:54 -0500 Subject: [PATCH 19/52] Fixes custom slices not inheriting name --- code/modules/food_and_drinks/food/customizables.dm | 1 - code/modules/food_and_drinks/food/snacks.dm | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/food_and_drinks/food/customizables.dm b/code/modules/food_and_drinks/food/customizables.dm index 3a6c2d91f6..7a1682aedd 100644 --- a/code/modules/food_and_drinks/food/customizables.dm +++ b/code/modules/food_and_drinks/food/customizables.dm @@ -135,7 +135,6 @@ /obj/item/weapon/reagent_containers/food/snacks/customizable/initialize_slice(obj/item/weapon/reagent_containers/food/snacks/slice, reagents_per_slice) ..() - slice.name = "[customname] [initial(slice.name)]" slice.filling_color = filling_color slice.update_overlays(src) diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index 148541922f..95d3a7993b 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -215,8 +215,9 @@ /obj/item/weapon/reagent_containers/food/snacks/proc/initialize_slice(obj/item/weapon/reagent_containers/food/snacks/slice, reagents_per_slice) slice.create_reagents(slice.volume) reagents.trans_to(slice,reagents_per_slice) - if( name != initial(name) || desc != initial(desc) ) - slice.name = "slice of [src]" + if(name != initial(name)) + slice.name = "slice of [name]" + if(desc != initial(desc)) slice.desc = "[desc]" /obj/item/weapon/reagent_containers/food/snacks/proc/generate_trash(atom/location) From 76c6a54d7e095ac03e48e7749de6a06ab6652aac Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:37:57 -0500 Subject: [PATCH 20/52] Gives lavaland syndicates the syndicate faction. --- code/modules/ruins/lavaland_ruin_code.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index ccc690350a..c9f2ee6360 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -143,6 +143,7 @@ has_id = 1 flavour_text = "You are a syndicate agent, employed in a top secret research facility developing biological weapons. Unfortunately, your hated enemy, Nanotrasen, has begun mining in this sector. Continue your research as best you can, and try to keep a low profile. Do not abandon the base without good cause. The base is rigged with explosives should the worst happen, do not let the base fall into enemy hands!" id_access_list = list(GLOB.access_syndicate) + faction = list("syndicate") /obj/effect/mob_spawn/human/lavaland_syndicate/comms name = "Syndicate Comms Agent" From 62c22a5243c003bf16084932055c79f5838fbdb6 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 16:38:05 -0500 Subject: [PATCH 21/52] Fixes uplink pens not showing in traitor menu --- code/datums/mind.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index c223d3b315..dd7a7ba38a 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1330,7 +1330,7 @@ obj_count++ /datum/mind/proc/find_syndicate_uplink() - var/list/L = current.get_contents() + var/list/L = current.GetAllContents() for (var/obj/item/I in L) if (I.hidden_uplink) return I.hidden_uplink From 893eb9283392308b66cd5a460a47407fa4532e81 Mon Sep 17 00:00:00 2001 From: cebutris Date: Thu, 18 May 2017 18:04:42 -0400 Subject: [PATCH 22/52] no more free parasites --- code/modules/mob/living/simple_animal/guardian/guardian.dm | 1 + code/modules/uplink/uplink_item_cit.dm | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index fe6e22bcab..6d012a67e2 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -495,6 +495,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians if(candidates.len) theghost = pick(candidates) spawn_guardian(user, theghost.key) + qdel(src) else to_chat(user, "[failure_message]") used = FALSE diff --git a/code/modules/uplink/uplink_item_cit.dm b/code/modules/uplink/uplink_item_cit.dm index b835a21a8c..332254aa37 100644 --- a/code/modules/uplink/uplink_item_cit.dm +++ b/code/modules/uplink/uplink_item_cit.dm @@ -16,6 +16,7 @@ It requires an organic host as a home base and source of fuel." //This is the description of the actual injector. Feel free to change this for uplink purposes// item = /obj/item/weapon/storage/box/syndie_kit/holoparasite refundable = TRUE - cost = 10 //I'm working off the borer. Price subject to change + cost = 15 //I'm working off the borer. Price subject to change surplus = 20 //Nobody needs a ton of parasites - exclude_modes = list(/datum/game_mode/nuclear) \ No newline at end of file + exclude_modes = list(/datum/game_mode/nuclear) + refund_path = /obj/item/weapon/guardiancreator/tech/choose/traitor \ No newline at end of file From 348d9497bd14de8be5af05401a3ad5be793ac2cc Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 22:37:57 -0500 Subject: [PATCH 23/52] Automatic changelog generation for PR #1029 [ci skip] --- html/changelogs/AutoChangeLog-pr-1029.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-1029.yml diff --git a/html/changelogs/AutoChangeLog-pr-1029.yml b/html/changelogs/AutoChangeLog-pr-1029.yml new file mode 100644 index 0000000000..5557069616 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-1029.yml @@ -0,0 +1,4 @@ +author: "Lordpidey" +delete-after: True +changes: + - rscadd: "Toy toolboxes with realistic rumbling action have been added to arcade prizes." From a466fffaa7b245311a397f601709d93e56ccc8f8 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 22:38:48 -0500 Subject: [PATCH 24/52] Automatic changelog generation for PR #1030 [ci skip] --- html/changelogs/AutoChangeLog-pr-1030.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-1030.yml diff --git a/html/changelogs/AutoChangeLog-pr-1030.yml b/html/changelogs/AutoChangeLog-pr-1030.yml new file mode 100644 index 0000000000..40e5e1abd0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-1030.yml @@ -0,0 +1,4 @@ +author: "Tacolizard Forever: Plasmaman Powercreep" +delete-after: True +changes: + - tweak: "Plasmaman tanks are the same size as emergency oxygen tanks." From 28d125eeaaa0c32d642c43b306a37f4170cf8d50 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 22:39:26 -0500 Subject: [PATCH 25/52] Automatic changelog generation for PR #1031 [ci skip] --- html/changelogs/AutoChangeLog-pr-1031.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-1031.yml diff --git a/html/changelogs/AutoChangeLog-pr-1031.yml b/html/changelogs/AutoChangeLog-pr-1031.yml new file mode 100644 index 0000000000..bb9c78ddb3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-1031.yml @@ -0,0 +1,4 @@ +author: "CitadelStationBot" +delete-after: True +changes: + - rscadd: "Peacekeeper cyborgs now have projectile dampening fields." From 5030026db7f78791e7dfba15b39902cb0dc852b2 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 22:40:44 -0500 Subject: [PATCH 26/52] Automatic changelog generation for PR #1038 [ci skip] --- html/changelogs/AutoChangeLog-pr-1038.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-1038.yml diff --git a/html/changelogs/AutoChangeLog-pr-1038.yml b/html/changelogs/AutoChangeLog-pr-1038.yml new file mode 100644 index 0000000000..85daa80c5e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-1038.yml @@ -0,0 +1,8 @@ +author: "coiax" +delete-after: True +changes: + - rscadd: "Various vending machines, when shooting their inventory at nearby +people, will \"demonstrate their products features\". This means that if a +cigarette vending machine throws a lighter at you, it will be on. +Vending machines also choose random products when throwing, rather than +the first available one." From 11b4a00322c07125e7cf75c44b89c368aa98da0e Mon Sep 17 00:00:00 2001 From: LetterJay Date: Thu, 18 May 2017 22:43:55 -0500 Subject: [PATCH 27/52] self merge --- code/game/machinery/hologram.dm | 2 +- code/game/machinery/hologram.dm.rej | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 code/game/machinery/hologram.dm.rej diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 2c2e39c4d7..7ad6c3a1f7 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -295,7 +295,7 @@ GLOBAL_LIST_EMPTY(holopads) Hologram.Impersonation = user - Hologram.language_holder = user.get_language_holder() + Hologram.copy_known_languages_from(user,replace = TRUE) Hologram.mouse_opacity = 0//So you can't click on it. Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. diff --git a/code/game/machinery/hologram.dm.rej b/code/game/machinery/hologram.dm.rej deleted file mode 100644 index fbb5cbc866..0000000000 --- a/code/game/machinery/hologram.dm.rej +++ /dev/null @@ -1,10 +0,0 @@ -diff a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm (rejected hunks) -@@ -295,7 +295,7 @@ GLOBAL_LIST_EMPTY(holopads) - Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY) - Hologram.Impersonation = user - -- Hologram.language_holder = user.get_language_holder() -+ Hologram.copy_known_languages_from(user,replace = TRUE) - Hologram.mouse_opacity = 0//So you can't click on it. - Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. - Hologram.anchored = 1//So space wind cannot drag it. From 514d29043e3f740df93128873ee02b5d0bd2c897 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 22:44:46 -0500 Subject: [PATCH 28/52] Automatic changelog generation for PR #1040 [ci skip] --- html/changelogs/AutoChangeLog-pr-1040.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-1040.yml diff --git a/html/changelogs/AutoChangeLog-pr-1040.yml b/html/changelogs/AutoChangeLog-pr-1040.yml new file mode 100644 index 0000000000..55633298a8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-1040.yml @@ -0,0 +1,4 @@ +author: "Joan" +delete-after: True +changes: + - rscadd: "Proselytizing alloy shards will now proselytize all the shards in the tile, instead of requiring you to proselytize each one at a time." From 45bc120501d164d11abeabb91ba1858e8ca76c2e Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 06:21:06 -0500 Subject: [PATCH 29/52] fixes aimed spells not recharging --- code/modules/spells/spell_types/aimed.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm index cdac152ed2..b06f69c189 100644 --- a/code/modules/spells/spell_types/aimed.dm +++ b/code/modules/spells/spell_types/aimed.dm @@ -24,6 +24,7 @@ if(charge_type == "recharge") var/refund_percent = current_amount/projectile_amount charge_counter = charge_max * refund_percent + start_recharge() remove_ranged_ability(msg) else msg = "[active_msg]Left-click to shoot it at a target!" From a56d1fcb9b9fccd032ae06063ca4ea02be018798 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 06:21:10 -0500 Subject: [PATCH 30/52] Fix choice for golems transferring between shells --- code/game/objects/structures/ghost_role_spawners.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index 82378b094c..05414f0a4d 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -149,8 +149,8 @@ /obj/effect/mob_spawn/human/golem/attack_hand(mob/user) if(isgolem(user) && can_transfer) - var/transfer = alert("Transfer your soul to [src]? (Warning, your old body will die!)",,"Yes","No") - if(!transfer) + var/transfer_choice = alert("Transfer your soul to [src]? (Warning, your old body will die!)",,"Yes","No") + if(transfer_choice != "Yes") return log_game("[user.ckey] golem-swapped into [src]") user.visible_message("A faint light leaves [user], moving to [src] and animating it!","You leave your old body behind, and transfer into [src]!") From b943780a8c970ed1c77565eb903a377329646036 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 06:25:19 -0500 Subject: [PATCH 31/52] If ratvar is alive, turning clockwork floors into clockwork walls is fast --- .../clock_helpers/proselytizer_helpers.dm | 2 +- .../clock_items/clockwork_proselytizer.dm | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm b/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm index 0566fb2d7a..5ede091ca4 100644 --- a/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm +++ b/code/game/gamemodes/clock_cult/clock_helpers/proselytizer_helpers.dm @@ -53,7 +53,7 @@ to_chat(user, "Something is in the way, preventing you from proselytizing [src] into a clockwork wall.") return TRUE var/operation_time = 100 - if(proselytizer.speed_multiplier > 0) + if(!GLOB.ratvar_awakens && proselytizer.speed_multiplier > 0) //if ratvar isn't awake, this always takes 10 seconds operation_time /= proselytizer.speed_multiplier return list("operation_time" = operation_time, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH) diff --git a/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm b/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm index 1d34ea2814..aa174adcd8 100644 --- a/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm +++ b/code/game/gamemodes/clock_cult/clock_items/clockwork_proselytizer.dm @@ -246,8 +246,13 @@ return TRUE return FALSE +//The following three procs are heavy wizardry. +//What these procs do is they take an existing list of values, which they then modify. +//This(modifying an existing object, in this case the list) is the only way to get information OUT of a do_after callback, which this is used as. + +//The proselytize check proc. /obj/item/clockwork/clockwork_proselytizer/proc/proselytize_checks(list/proselytize_values, atom/target, expected_type, mob/user, silent) //checked constantly while proselytizing - if(!islist(proselytize_values) || !target || QDELETED(target) || !user) + if(!islist(proselytize_values) || QDELETED(target) || QDELETED(user)) return FALSE if(repairing || recharging) return FALSE @@ -266,11 +271,8 @@ return TRUE //The repair check proc. -//Is dark magic. Can probably kill you. -//What this proc does is it takes an existing list of values, which it modifies. -//This(modifying an existing object) is the only way to get information OUT of a do_after callback, which this is used as. /obj/item/clockwork/clockwork_proselytizer/proc/proselytizer_repair_checks(list/repair_values, atom/target, mob/user, silent) //Exists entirely to avoid an otherwise unreadable series of checks. - if(!islist(repair_values) || !target || QDELETED(target) || !user) + if(!islist(repair_values) || QDELETED(target) || QDELETED(user)) return FALSE if(isliving(target)) //standard checks for if we can affect the target var/mob/living/L = target @@ -309,9 +311,9 @@ return FALSE return TRUE -//checked constantly while charging from a sigil +//The sigil charge check proc. /obj/item/clockwork/clockwork_proselytizer/proc/sigil_charge_checks(list/charge_values, obj/effect/clockwork/sigil/transmission/sigil, mob/user, silent) - if(!islist(charge_values) || !sigil || QDELETED(sigil) || !user) + if(!islist(charge_values) || QDELETED(sigil) || QDELETED(user)) return FALSE if(can_use_power(RATVAR_POWER_CHECK)) return FALSE From c36eee94bdec6f9b19ebbcb60af02fe74aecf248 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 06:25:23 -0500 Subject: [PATCH 32/52] Adds modified syringe guns --- .../objects/items/weapons/dna_injector.dm | 8 ++-- .../modules/projectiles/ammunition/special.dm | 19 ++++++++++ code/modules/projectiles/guns/syringe_gun.dm | 38 ++++++++++++++++--- .../modules/projectiles/projectile/bullets.dm | 33 +++++++++++++--- code/modules/uplink/uplink_item.dm | 7 ++++ 5 files changed, 89 insertions(+), 16 deletions(-) diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index 00d6a55f2e..c50ef26b81 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -50,9 +50,7 @@ M.dna.uni_identity = merge_text(M.dna.uni_identity, fields["UI"]) M.updateappearance(mutations_overlay_update=1) log_attack(log_msg) - else - to_chat(user, "It appears that [M] does not have compatible DNA.") - return + return TRUE /obj/item/weapon/dnainjector/attack(mob/target, mob/user) if(!user.IsAdvancedToolUser()) @@ -79,7 +77,9 @@ add_logs(user, target, "injected", src) - inject(target, user) //Now we actually do the heavy lifting. + if(!inject(target, user)) //Now we actually do the heavy lifting. + to_chat(user, "It appears that [target] does not have compatible DNA.") + used = 1 icon_state = "dnainjector0" desc += " This one is used up." diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special.dm index 68681c1124..16ec87e043 100644 --- a/code/modules/projectiles/ammunition/special.dm +++ b/code/modules/projectiles/ammunition/special.dm @@ -64,6 +64,25 @@ qdel(S) ..() +/obj/item/ammo_casing/dnainjector + name = "rigged syringe gun spring" + desc = "A high-power spring that throws DNA injectors." + projectile_type = /obj/item/projectile/bullet/dnainjector + firing_effect_type = null + +/obj/item/ammo_casing/dnainjector/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") + if(!BB) + return + if(istype(loc, /obj/item/weapon/gun/syringe/dna)) + var/obj/item/weapon/gun/syringe/dna/SG = loc + if(!SG.syringes.len) + return + + var/obj/item/weapon/dnainjector/S = popleft(SG.syringes) + var/obj/item/projectile/bullet/dnainjector/D = BB + S.forceMove(D) + D.injector = S + ..() /obj/item/ammo_casing/energy/c3dbullet projectile_type = /obj/item/projectile/bullet/midbullet3 diff --git a/code/modules/projectiles/guns/syringe_gun.dm b/code/modules/projectiles/guns/syringe_gun.dm index 6da113b6e3..09547b0d98 100644 --- a/code/modules/projectiles/guns/syringe_gun.dm +++ b/code/modules/projectiles/guns/syringe_gun.dm @@ -49,18 +49,18 @@ return 1 -/obj/item/weapon/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = 1) +/obj/item/weapon/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = TRUE) if(istype(A, /obj/item/weapon/reagent_containers/syringe)) if(syringes.len < max_syringes) if(!user.transferItemToLoc(A, src)) - return + return FALSE to_chat(user, "You load [A] into \the [src].") - syringes.Add(A) + syringes += A recharge_newshot() - return 1 + return TRUE else - to_chat(usr, "[src] cannot hold more syringes!") - return 0 + to_chat(user, "[src] cannot hold more syringes!") + return FALSE /obj/item/weapon/gun/syringe/rapidsyringe name = "rapid syringe gun" @@ -78,3 +78,29 @@ force = 2 //Also very weak because it's smaller suppressed = 1 //Softer fire sound can_unsuppress = 0 //Permanently silenced + +/obj/item/weapon/gun/syringe/dna + name = "modified syringe gun" + desc = "A syringe gun that has been modified to fit DNA injectors instead of normal syringes." + origin_tech = "combat=2;syndicate=2;biotech=3" + +/obj/item/weapon/gun/syringe/dna/Initialize() + . = ..() + chambered = new /obj/item/ammo_casing/dnainjector(src) + +/obj/item/weapon/gun/syringe/dna/attackby(obj/item/A, mob/user, params, show_msg = TRUE) + if(istype(A, /obj/item/weapon/dnainjector)) + var/obj/item/weapon/dnainjector/D = A + if(D.used) + to_chat(user, "This injector is used up!") + return + if(syringes.len < max_syringes) + if(!user.transferItemToLoc(D, src)) + return FALSE + to_chat(user, "You load \the [D] into \the [src].") + syringes += D + recharge_newshot() + return TRUE + else + to_chat(user, "[src] cannot hold more syringes!") + return FALSE \ No newline at end of file diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 7ef0d31561..cceb77bee1 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -190,7 +190,7 @@ name = "dart" icon_state = "cbbolt" damage = 6 - var/piercing = 0 + var/piercing = FALSE /obj/item/projectile/bullet/dart/New() ..() @@ -201,15 +201,15 @@ if(iscarbon(target)) var/mob/living/carbon/M = target if(blocked != 100) // not completely blocked - if(M.can_inject(null, 0, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. + if(M.can_inject(null, FALSE, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. ..() reagents.reaction(M, INJECT) reagents.trans_to(M, reagents.total_volume) - return 1 + return TRUE else blocked = 100 - target.visible_message("The [name] was deflected!", \ - "You were protected against the [name]!") + target.visible_message("\The [src] was deflected!", \ + "You were protected against \the [src]!") ..(target, blocked) reagents.set_reacting(TRUE) @@ -240,7 +240,28 @@ nodamage = 1 . = ..() // Execute the rest of the code. - +/obj/item/projectile/bullet/dnainjector + name = "\improper DNA injector" + icon_state = "syringeproj" + var/obj/item/weapon/dnainjector/injector + +/obj/item/projectile/bullet/dnainjector/on_hit(atom/target, blocked = 0) + if(iscarbon(target)) + var/mob/living/carbon/M = target + if(blocked != 100) + if(M.can_inject(null, FALSE, def_zone, FALSE)) + if(injector.inject(M, firer)) + QDEL_NULL(injector) + return TRUE + else + blocked = 100 + target.visible_message("\The [src] was deflected!", \ + "You were protected against \the [src]!") + return ..() + +/obj/item/projectile/bullet/dnainjector/Destroy() + QDEL_NULL(injector) + return ..() //// SNIPER BULLETS diff --git a/code/modules/uplink/uplink_item.dm b/code/modules/uplink/uplink_item.dm index d6e6eae1ca..3f5b4b4ca4 100644 --- a/code/modules/uplink/uplink_item.dm +++ b/code/modules/uplink/uplink_item.dm @@ -1299,6 +1299,13 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. cost = 2 restricted_roles = list("Curator") limited_stock = 1 // please don't spam deadchat + +/datum/uplink_item/role_restricted/modified_syringe_gun + name = "Modified Syringe Gun" + desc = "A syringe gun that fires DNA injectors instead of normal syringes." + item = /obj/item/weapon/gun/syringe/dna + cost = 14 + restricted_roles = list("Geneticist", "Chief Medical Officer") // Pointless /datum/uplink_item/badass From 44671daa84f432cf0db117c088298c5c5203815b Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 08:04:42 -0500 Subject: [PATCH 33/52] Automatic changelog generation for PR #1056 [ci skip] --- html/changelogs/AutoChangeLog-pr-1056.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-1056.yml diff --git a/html/changelogs/AutoChangeLog-pr-1056.yml b/html/changelogs/AutoChangeLog-pr-1056.yml new file mode 100644 index 0000000000..abf47dddbb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-1056.yml @@ -0,0 +1,4 @@ +author: "Swindly" +delete-after: True +changes: + - rscadd: "Added modified syringe guns. They fire DNA injectors. Geneticists and CMOs can buy them from the traitor uplink for 14 TC." From 88f1b8c428d83622dff0e164fd833da8783f9b66 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 08:05:39 -0500 Subject: [PATCH 34/52] Automatic changelog generation for PR #1054 [ci skip] --- html/changelogs/AutoChangeLog-pr-1054.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-1054.yml diff --git a/html/changelogs/AutoChangeLog-pr-1054.yml b/html/changelogs/AutoChangeLog-pr-1054.yml new file mode 100644 index 0000000000..130e19fc5c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-1054.yml @@ -0,0 +1,4 @@ +author: "CitadelStationBot" +delete-after: True +changes: + - bugfix: "Golems touching a shell can now choose to stay in their own body." From 4430e7d0c3ac3612536e6cdf23f826ef33fe1e98 Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 12:31:59 -0500 Subject: [PATCH 35/52] Fixes having more movespeed when xenos hug themselves --- code/modules/mob/living/carbon/alien/status_procs.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/carbon/alien/status_procs.dm b/code/modules/mob/living/carbon/alien/status_procs.dm index 653b05ae5a..b63d233016 100644 --- a/code/modules/mob/living/carbon/alien/status_procs.dm +++ b/code/modules/mob/living/carbon/alien/status_procs.dm @@ -17,4 +17,4 @@ /mob/living/carbon/alien/AdjustStunned(amount, updating = 1, ignore_canstun = 0) . = ..() if(!.) - move_delay_add = min(move_delay_add + round(amount / 2), 10) + move_delay_add = Clamp(move_delay_add + round(amount/2), 0, 10) From 778d809ff3eb6d1eb5cb390d1176aa593166041a Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 13:48:39 -0500 Subject: [PATCH 36/52] Fixes gravity generator admin spam --- code/modules/power/gravitygenerator.dm | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 7a148067d3..a1d544c2e4 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -304,16 +304,17 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne // Sound the alert if gravity was just enabled or disabled. var/alert = 0 var/area/A = get_area(src) - if(on && SSticker.IsRoundInProgress()) // If we turned on and the game is live. - if(gravity_in_level() == 0) - alert = 1 - investigate_log("was brought online and is now producing gravity for this level.", "gravity") - message_admins("The gravity generator was brought online [A][ADMIN_COORDJMP(src)]") - else - if(gravity_in_level() == 1) - alert = 1 - investigate_log("was brought offline and there is now no gravity for this level.", "gravity") - message_admins("The gravity generator was brought offline with no backup generator. [A][ADMIN_COORDJMP(src)]") + if(SSticker.IsRoundInProgress()) + if(on) // If we turned on and the game is live. + if(gravity_in_level() == 0) + alert = 1 + investigate_log("was brought online and is now producing gravity for this level.", "gravity") + message_admins("The gravity generator was brought online [A][ADMIN_COORDJMP(src)]") + else + if(gravity_in_level() == 1) + alert = 1 + investigate_log("was brought offline and there is now no gravity for this level.", "gravity") + message_admins("The gravity generator was brought offline with no backup generator. [A][ADMIN_COORDJMP(src)]") update_icon() update_list() From 461804e635641d4a4e4647b8f7a823c921ab087c Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Fri, 19 May 2017 13:51:47 -0500 Subject: [PATCH 37/52] Pulse Weapons Sprite Update --- code/modules/projectiles/guns/energy/pulse.dm | 3 ++- icons/obj/guns/energy.dmi | Bin 31993 -> 32651 bytes icons/turf/dirt.dmi | Bin 0 -> 2222 bytes 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 icons/turf/dirt.dmi diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index 951b5aeb62..b938e399df 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -5,6 +5,7 @@ item_state = null w_class = WEIGHT_CLASS_BULKY force = 10 + modifystate = TRUE flags = CONDUCT slot_flags = SLOT_BACK ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) @@ -39,7 +40,7 @@ w_class = WEIGHT_CLASS_NORMAL slot_flags = SLOT_BELT icon_state = "pulse_carbine" - item_state = "pulse" + item_state = null cell_type = "/obj/item/weapon/stock_parts/cell/pulse/carbine" can_flashlight = 1 flight_x_offset = 18 diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi index 3916befb1255808bbc5428a4615685169a2a753c..71b1474a660cb9a4a3b628ad5b1aa7860b924a5d 100644 GIT binary patch literal 32651 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 CI>~YX literal 31993 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�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